Snap for 9430503 from 31fbf2d91623e6fab9a6bb6a97759c338608f9de to t-keystone-qcom-release

Change-Id: I8953cb65f1c419ddf95dd3bd2ec710f57c710df9
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..06e27d7
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,72 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [ main ]
+  schedule:
+    - cron: '39 21 * * 4'
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+    permissions:
+      actions: read
+      contents: read
+      security-events: write
+
+    strategy:
+      fail-fast: false
+      matrix:
+        language: [ 'python' ]
+        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v3
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v2
+      with:
+        languages: ${{ matrix.language }}
+        # If you wish to specify custom queries, you can do so here or in a config file.
+        # By default, queries listed here will override any specified in a config file.
+        # Prefix the list here with "+" to use these queries and those in the config file.
+        
+        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+        # queries: security-extended,security-and-quality
+
+        
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v2
+
+    # ℹī¸ Command-line programs to run using the OS shell.
+    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+
+    #   If the Autobuild fails above, remove it and uncomment the following three lines. 
+    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
+
+    # - run: |
+    #   echo "Run, Build Application using script"
+    #   ./location_of_script_within_repo/buildscript.sh
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/python-build-test.yml b/.github/workflows/python-build-test.yml
new file mode 100644
index 0000000..062f153
--- /dev/null
+++ b/.github/workflows/python-build-test.yml
@@ -0,0 +1,34 @@
+# Build and test the python package
+name: Python build and test
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+permissions:
+  contents: read
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python 3.10
+      uses: actions/setup-python@v3
+      with:
+        python-version: "3.10"
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        python -m pip install ".[test,development,documentation]"
+    - name: Test with pytest
+      run: |
+        pytest
+    - name: Build
+      run: |
+        inv build
+        inv mkdocs
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5cb29bb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+.eggs/
+build/
+dist/
+*.egg-info/
+*~
+bumble/__pycache__
+docs/mkdocs/site
+tests/__pycache__
+test-results.xml
+bumble/transport/__pycache__
+bumble/profiles/__pycache__
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..7d26b07
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,28 @@
+package {
+    default_applicable_licenses: ["external_python_bumble_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "external_python_bumble_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
+}
+
+python_library_host {
+    name: "bumble",
+    srcs: [
+        "bumble/*.py",
+        "bumble/profiles/*.py",
+        "bumble/transport/*.py",
+    ],
+    libs: [
+        "py-cryptography",
+    ],
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e5c3b28
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA). You (or your employer) retain the copyright to your
+contribution; this simply gives us permission to use and redistribute your
+contributions as part of the project. Head over to
+<https://cla.developers.google.com/> to see your current agreements on file or
+to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code Reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..30444f1
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "Bumble"
+description:
+    "Bumble is a full-featured Bluetooth stack written entirely in Python."
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://google.github.io/bumble/"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/google/bumble"
+  }
+  version: "c66b357de6908cf3680d83a73c6744451e2d0fa0"
+  last_upgrade_date { year: 2022 month: 7 day: 25 }
+  license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..b008db5
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+boccongibod@google.com
+girardier@google.com
+licorne@google.com
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3370baf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+
+     _                 _     _
+    | |               | |   | |
+    | |__  _   _ ____ | |__ | | _____
+    |  _ \| | | |    \|  _ \| || ___ |
+    | |_) ) |_| | | | | |_) ) || ____|
+    |____/|____/|_|_|_|____/ \_)_____)
+
+Bluetooth Stack for Apps, Emulation, Test and Experimentation
+=============================================================
+
+<img src="docs/mkdocs/src/images/logo_framed.png" alt="drawing" width="200" height="200"/>
+
+Bumble is a full-featured Bluetooth stack written entirely in Python. It supports most of the common Bluetooth Low Energy (BLE) and Bluetooth Classic (BR/EDR) protocols and profiles, including GAP, L2CAP, ATT, GATT, SMP, SDP, RFCOMM, HFP, HID and A2DP. The stack can be used with physical radios via HCI over USB, UART, or the Linux VHCI, as well as virtual radios, including the virtual Bluetooth support of the Android emulator.
+
+## Documentation
+
+Browse the pre-built [Online Documentation](https://google.github.io/bumble/), 
+or see the documentation source under `docs/mkdocs/src`, or build the static HTML site from the markdown text with:
+```
+mkdocs build -f docs/mkdocs/mkdocs.yml 
+```
+
+## License
+
+Licensed under the [Apache 2.0](LICENSE) License.
+
+## Disclaimer
+
+This is not an official Google product.
+
+This library is in alpha and will be going through a lot of breaking changes. While releases will be stable enough for prototyping, experimentation and research, we do not recommend using it in any production environment yet.
+Expect bugs and sharp edges.
+Please help by trying it out, reporting bugs, and letting us know what you think!
diff --git a/apps/README.md b/apps/README.md
new file mode 100644
index 0000000..0abcf09
--- /dev/null
+++ b/apps/README.md
@@ -0,0 +1,51 @@
+Bumble Apps
+===========
+
+NOTE:
+To run python scripts from this directory when the Bumble package isn't installed in your environment,
+put .. in your PYTHONPATH: `export PYTHONPATH=..`
+
+
+Apps
+----
+
+## `show.py`
+Parse a file with HCI packets and print the details of each packet in a human readable form
+
+## `link_relay.py`
+Simple WebSocket relay for virtual RemoteLink instances to communicate with each other through.
+
+## `hci_bridge.py`
+This app acts as a simple bridge between two HCI transports, with a host on one side and
+a controller on the other. All the HCI packets bridged between the two are printed on the console
+for logging. This bridge also has the ability to short-circuit some HCI packets (respond to them
+with a fixed response instead of bridging them to the other side), which may be useful when used with
+a host that send custom HCI commands that the controller may not understand.
+
+### Usage
+```
+python hci_bridge.py <host-transport-spec> <controller-transport-spec> [command-short-circuit-list]
+```
+
+### Examples
+
+#### UDP to HCI UART
+```
+python hci_bridge.py udp:0.0.0.0:9000,127.0.0.1:9001 serial:/dev/tty.usbmodem0006839912171,1000000 0x3f:0x0070,0x3f:0x0074,0x3f:0x0077,0x3f:0x0078
+```
+
+#### PTY to Link Relay
+```
+python hci_bridge.py serial:emulated_uart_pty,1000000 link-relay:ws://127.0.0.1:10723/test
+```
+
+In this example, an emulator that exposes a PTY as an interface to its HCI UART is running as
+a Bluetooth host, and we are connecting it to a virtual controller attached to a link relay
+(through which the communication with other virtual controllers will be mediated).
+
+NOTE: this assumes you're running a Link Relay on port `10723`.
+
+## `console.py`
+A simple text-based-ui interactive Bluetooth device with GATT client capabilities.
+
+
diff --git a/apps/__init__.py b/apps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/apps/__init__.py
diff --git a/apps/console.py b/apps/console.py
new file mode 100644
index 0000000..3ac4957
--- /dev/null
+++ b/apps/console.py
@@ -0,0 +1,601 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Bumble Tool
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+from bumble.hci import HCI_Constant
+import os
+import os.path
+import logging
+import click
+from collections import OrderedDict
+import colors
+
+from bumble.core import UUID, AdvertisingData
+from bumble.device import Device, Connection, Peer
+from bumble.utils import AsyncRunner
+from bumble.transport import open_transport_or_link
+
+from prompt_toolkit import Application
+from prompt_toolkit.history import FileHistory
+from prompt_toolkit.completion import Completer, Completion, NestedCompleter
+from prompt_toolkit.key_binding import KeyBindings
+from prompt_toolkit.formatted_text import ANSI
+from prompt_toolkit.styles import Style
+from prompt_toolkit.filters import Condition
+from prompt_toolkit.widgets import TextArea, Frame
+from prompt_toolkit.widgets.toolbars import FormattedTextToolbar
+from prompt_toolkit.layout import (
+    Layout,
+    HSplit,
+    Window,
+    CompletionsMenu,
+    Float,
+    FormattedTextControl,
+    FloatContainer,
+    ConditionalContainer
+)
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+BUMBLE_USER_DIR        = os.path.expanduser('~/.bumble')
+DEFAULT_PROMPT_HEIGHT  = 20
+DEFAULT_RSSI_BAR_WIDTH = 20
+DISPLAY_MIN_RSSI       = -100
+DISPLAY_MAX_RSSI       = -30
+
+# -----------------------------------------------------------------------------
+# Globals
+# -----------------------------------------------------------------------------
+App = None
+
+
+# -----------------------------------------------------------------------------
+# Console App
+# -----------------------------------------------------------------------------
+class ConsoleApp:
+    def __init__(self):
+        self.known_addresses = set()
+        self.known_attributes = []
+        self.device = None
+        self.connected_peer = None
+        self.top_tab = 'scan'
+
+        style = Style.from_dict({
+            'output-field': 'bg:#000044 #ffffff',
+            'input-field':  'bg:#000000 #ffffff',
+            'line':         '#004400',
+            'error':        'fg:ansired'
+        })
+
+        class LiveCompleter(Completer):
+            def __init__(self, words):
+                self.words = words
+
+            def get_completions(self, document, complete_event):
+                prefix = document.text_before_cursor.upper()
+                for word in [x for x in self.words if x.upper().startswith(prefix)]:
+                    yield Completion(word, start_position=-len(prefix))
+
+        def make_completer():
+            return NestedCompleter.from_nested_dict({
+                'scan': {
+                    'on': None,
+                    'off': None
+                },
+                'advertise': {
+                    'on': None,
+                    'off': None
+                },
+                'show': {
+                    'scan': None,
+                    'services': None,
+                    'attributes': None,
+                    'log': None
+                },
+                'connect': LiveCompleter(self.known_addresses),
+                'update-parameters': None,
+                'encrypt': None,
+                'disconnect': None,
+                'discover': {
+                    'services': None,
+                    'attributes': None
+                },
+                'read': LiveCompleter(self.known_attributes),
+                'write': LiveCompleter(self.known_attributes),
+                'quit': None,
+                'exit': None
+            })
+
+        self.input_field = TextArea(
+            height=1,
+            prompt="> ",
+            multiline=False,
+            wrap_lines=False,
+            completer=make_completer(),
+            history=FileHistory(os.path.join(BUMBLE_USER_DIR, 'history'))
+        )
+
+        self.input_field.accept_handler = self.accept_input
+
+        self.output_height = 7
+        self.output_lines = []
+        self.output = FormattedTextControl()
+        self.scan_results_text = FormattedTextControl()
+        self.services_text = FormattedTextControl()
+        self.attributes_text = FormattedTextControl()
+        self.log_text = FormattedTextControl()
+        self.log_height = 20
+        self.log_lines = []
+
+        container = HSplit([
+            ConditionalContainer(
+                Frame(Window(self.scan_results_text), title='Scan Results'),
+                filter=Condition(lambda: self.top_tab == 'scan')
+            ),
+            ConditionalContainer(
+                Frame(Window(self.services_text), title='Services'),
+                filter=Condition(lambda: self.top_tab == 'services')
+            ),
+            ConditionalContainer(
+                Frame(Window(self.attributes_text), title='Attributes'),
+                filter=Condition(lambda: self.top_tab == 'attributes')
+            ),
+            ConditionalContainer(
+                Frame(Window(self.log_text), title='Log'),
+                filter=Condition(lambda: self.top_tab == 'log')
+            ),
+            Frame(Window(self.output), height=self.output_height),
+            # HorizontalLine(),
+            FormattedTextToolbar(text=self.get_status_bar_text, style='reverse'),
+            self.input_field
+        ])
+
+        container = FloatContainer(
+            container,
+            floats=[
+                Float(
+                    xcursor=True,
+                    ycursor=True,
+                    content=CompletionsMenu(max_height=16, scroll_offset=1),
+                ),
+            ],
+        )
+
+        layout = Layout(container, focused_element=self.input_field)
+
+        kb = KeyBindings()
+        @kb.add("c-c")
+        @kb.add("c-q")
+        def _(event):
+            event.app.exit()
+
+        self.ui = Application(
+            layout=layout,
+            style=style,
+            key_bindings=kb,
+            full_screen=True
+        )
+
+    async def run_async(self, device_config, transport):
+        async with await open_transport_or_link(transport) as (hci_source, hci_sink):
+            if device_config:
+                self.device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
+            else:
+                self.device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+            self.device.listener = DeviceListener(self)
+            await self.device.power_on()
+
+            # Run the UI
+            await self.ui.run_async()
+
+    def add_known_address(self, address):
+        self.known_addresses.add(address)
+
+    def accept_input(self, buff):
+        if len(self.input_field.text) == 0:
+            return
+        self.append_to_output([('', '* '), ('ansicyan', self.input_field.text)], False)
+        self.ui.create_background_task(self.command(self.input_field.text))
+
+    def get_status_bar_text(self):
+        scanning = "ON" if self.device and self.device.is_scanning else "OFF"
+
+        connection_state = 'NONE'
+        encryption_state = ''
+
+        if self.device:
+            if self.device.is_connecting:
+                connection_state = 'CONNECTING'
+            elif self.connected_peer:
+                connection = self.connected_peer.connection
+                connection_parameters = f'{connection.parameters.connection_interval}/{connection.parameters.connection_latency}/{connection.parameters.supervision_timeout}'
+                connection_state = f'{connection.peer_address} {connection_parameters} {connection.data_length}'
+                encryption_state = 'ENCRYPTED' if connection.is_encrypted else 'NOT ENCRYPTED'
+
+        return [
+            ('ansigreen', f' SCAN: {scanning} '),
+            ('', '  '),
+            ('ansiblue', f' CONNECTION: {connection_state} '),
+            ('', '  '),
+            ('ansimagenta', f' {encryption_state} ')
+        ]
+
+    def show_error(self, title, details = None):
+        appended = [('class:error', title)]
+        if details:
+            appended.append(('', f' {details}'))
+        self.append_to_output(appended)
+
+    def show_scan_results(self, scan_results):
+        max_lines = 40  # TEMP
+        lines = []
+        keys = list(scan_results.keys())[:max_lines]
+        for key in keys:
+            lines.append(scan_results[key].to_display_string())
+        self.scan_results_text.text = ANSI('\n'.join(lines))
+        self.ui.invalidate()
+
+    def show_services(self, services):
+        lines = []
+        del self.known_attributes[:]
+        for service in services:
+            lines.append(('ansicyan', str(service) + '\n'))
+
+            for characteristic in service.characteristics:
+                lines.append(('ansimagenta', '  ' + str(characteristic) + '\n'))
+                self.known_attributes.append(f'{service.uuid.to_hex_str()}.{characteristic.uuid.to_hex_str()}')
+                self.known_attributes.append(f'*.{characteristic.uuid.to_hex_str()}')
+                self.known_attributes.append(f'#{characteristic.handle:X}')
+                for descriptor in characteristic.descriptors:
+                    lines.append(('ansigreen', '    ' + str(descriptor) + '\n'))
+
+        self.services_text.text = lines
+        self.ui.invalidate()
+
+    async def show_attributes(self, attributes):
+        lines = []
+
+        for attribute in attributes:
+            lines.append(('ansicyan', f'{attribute}\n'))
+
+        self.attributes_text.text = lines
+        self.ui.invalidate()
+
+    def append_to_output(self, line, invalidate=True):
+        if type(line) is str:
+            line = [('', line)]
+        self.output_lines = self.output_lines[-(self.output_height - 3):]
+        self.output_lines.append(line)
+        formatted_text = []
+        for line in self.output_lines:
+            formatted_text += line
+            formatted_text.append(('', '\n'))
+        self.output.text = formatted_text
+        if invalidate:
+            self.ui.invalidate()
+
+    def append_to_log(self, lines, invalidate=True):
+        self.log_lines.extend(lines.split('\n'))
+        self.log_lines = self.log_lines[-(self.log_height - 3):]
+        self.log_text.text = ANSI('\n'.join(self.log_lines))
+        if invalidate:
+            self.ui.invalidate()
+
+    async def discover_services(self):
+        if not self.connected_peer:
+            self.show_error('not connected')
+            return
+
+        # Discover all services, characteristics and descriptors
+        self.append_to_output('discovering services...')
+        await self.connected_peer.discover_services()
+        self.append_to_output(f'found {len(self.connected_peer.services)} services, discovering charateristics...')
+        await self.connected_peer.discover_characteristics()
+        self.append_to_output('found characteristics, discovering descriptors...')
+        for service in self.connected_peer.services:
+            for characteristic in service.characteristics:
+                await self.connected_peer.discover_descriptors(characteristic)
+        self.append_to_output('discovery completed')
+
+        self.show_services(self.connected_peer.services)
+
+    async def discover_attributes(self):
+        if not self.connected_peer:
+            self.show_error('not connected')
+            return
+
+        # Discover all attributes
+        self.append_to_output('discovering attributes...')
+        attributes = await self.connected_peer.discover_attributes()
+        self.append_to_output(f'discovered {len(attributes)} attributes...')
+
+        await self.show_attributes(attributes)
+
+    async def command(self, command):
+        try:
+            (keyword, *params) = command.strip().split(' ', 1)
+            keyword = keyword.replace('-', '_').lower()
+            handler = getattr(self, f'do_{keyword}', None)
+            if handler:
+                await handler(params)
+                self.ui.invalidate()
+            else:
+                self.show_error('unknown command', keyword)
+        except Exception as error:
+            self.show_error(str(error))
+
+    async def do_scan(self, params):
+        if len(params) == 0:
+            # Toggle scanning
+            if self.device.is_scanning:
+                await self.device.stop_scanning()
+            else:
+                await self.device.start_scanning()
+        elif params[0] == 'on':
+            await self.device.start_scanning()
+            self.top_tab = 'scan'
+        elif params[0] == 'off':
+            await self.device.stop_scanning()
+        else:
+            self.show_error('unsupported arguments for scan command')
+
+    async def do_connect(self, params):
+        if len(params) != 1:
+            self.show_error('invalid syntax', 'expected connect <address>')
+            return
+
+        self.append_to_output('connecting...')
+        await self.device.connect(params[0])
+        self.top_tab = 'services'
+
+    async def do_disconnect(self, params):
+        if not self.connected_peer:
+            self.show_error('not connected')
+            return
+
+        await self.connected_peer.connection.disconnect()
+
+    async def do_update_parameters(self, params):
+        if len(params) != 1 or len(params[0].split('/')) != 3:
+            self.show_error('invalid syntax', 'expected update-parameters <interval-min>-<interval-max>/<latency>/<supervision>')
+            return
+
+        if not self.connected_peer:
+            self.show_error('not connected')
+            return
+
+        connection_intervals, connection_latency, supervision_timeout = params[0].split('/')
+        connection_interval_min, connection_interval_max = [int(x) for x in connection_intervals.split('-')]
+        connection_latency = int(connection_latency)
+        supervision_timeout = int(supervision_timeout)
+        await self.connected_peer.connection.update_parameters(
+            connection_interval_min,
+            connection_interval_max,
+            connection_latency,
+            supervision_timeout
+        )
+
+    async def do_encrypt(self, params):
+        if not self.connected_peer:
+            self.show_error('not connected')
+            return
+
+        await self.connected_peer.connection.encrypt()
+
+    async def do_advertise(self, params):
+        if len(params) == 0:
+            # Toggle advertising
+            if self.device.is_advertising:
+                await self.device.stop_advertising()
+            else:
+                await self.device.start_advertising()
+        elif params[0] == 'on':
+            await self.device.start_advertising()
+        elif params[0] == 'off':
+            await self.device.stop_advertising()
+        else:
+            self.show_error('unsupported arguments for advertise command')
+
+    async def do_show(self, params):
+        if params:
+            if params[0] in {'scan', 'services', 'attributes', 'log'}:
+                self.top_tab = params[0]
+                self.ui.invalidate()
+
+    async def do_discover(self, params):
+        if not params:
+            self.show_error('invalid syntax', 'expected discover services|attributes')
+            return
+
+        discovery_type = params[0]
+        if discovery_type == 'services':
+            await self.discover_services()
+        elif discovery_type == 'attributes':
+            await self.discover_attributes()
+
+    async def do_read(self, params):
+        if not self.connected_peer:
+            self.show_error('not connected')
+            return
+
+        if len(params) != 1:
+            self.show_error('invalid syntax', 'expected read <attribute>')
+            return
+
+        parts = params[0].split('.')
+        if len(parts) == 2:
+            service_uuid = UUID(parts[0]) if parts[0] != '*' else None
+            characteristic_uuid = UUID(parts[1])
+            for service in self.connected_peer.services:
+                if service_uuid is None or service.uuid == service_uuid:
+                    for characteristic in service.characteristics:
+                        if characteristic.uuid == characteristic_uuid:
+                            value = await self.connected_peer.read_value(characteristic)
+                            self.append_to_output(f'VALUE: {value}')
+                            return
+            self.show_error('no such characteristic')
+        elif len(parts) == 1:
+            if parts[0].startswith('#'):
+                attribute_handle = int(f'{parts[0][1:]}', 16)
+                value = await self.connected_peer.read_value(attribute_handle)
+                self.append_to_output(f'VALUE: {value}')
+                return
+        else:
+            self.show_error('no such characteristic')
+
+    async def do_exit(self, params):
+        self.ui.exit()
+
+    async def do_quit(self, params):
+        self.ui.exit()
+
+
+# -----------------------------------------------------------------------------
+# Device and Connection Listener
+# -----------------------------------------------------------------------------
+class DeviceListener(Device.Listener, Connection.Listener):
+    def __init__(self, app):
+        self.app = app
+        self.scan_results = OrderedDict()
+
+    @AsyncRunner.run_in_task()
+    async def on_connection(self, connection):
+        self.app.connected_peer = Peer(connection)
+        self.app.append_to_output(f'connected to {self.app.connected_peer}')
+        connection.listener = self
+
+    def on_disconnection(self, reason):
+        self.app.append_to_output(f'disconnected from {self.app.connected_peer}, reason: {HCI_Constant.error_name(reason)}')
+        self.app.connected_peer = None
+
+    def on_connection_parameters_update(self):
+        self.app.append_to_output(f'connection parameters update: {self.app.connected_peer.connection.parameters}')
+
+    def on_connection_phy_update(self):
+        self.app.append_to_output(f'connection phy update: {self.app.connected_peer.connection.phy}')
+
+    def on_connection_att_mtu_update(self):
+        self.app.append_to_output(f'connection att mtu update: {self.app.connected_peer.connection.att_mtu}')
+
+    def on_connection_encryption_change(self):
+        self.app.append_to_output(f'connection encryption change: {"encrypted" if self.app.connected_peer.connection.is_encrypted else "not encrypted"}')
+
+    def on_connection_data_length_change(self):
+        self.app.append_to_output(f'connection data length change: {self.app.connected_peer.connection.data_length}')
+
+    def on_advertisement(self, address, ad_data, rssi, connectable):
+        entry_key = f'{address}/{address.address_type}'
+        entry = self.scan_results.get(entry_key)
+        if entry:
+            entry.ad_data     = ad_data
+            entry.rssi        = rssi
+            entry.connectable = connectable
+        else:
+            self.app.add_known_address(str(address))
+            self.scan_results[entry_key] = ScanResult(address, address.address_type, ad_data, rssi, connectable)
+
+        self.app.show_scan_results(self.scan_results)
+
+
+# -----------------------------------------------------------------------------
+# Scanning
+# -----------------------------------------------------------------------------
+class ScanResult:
+    def __init__(self, address, address_type, ad_data, rssi, connectable):
+        self.address      = address
+        self.address_type = address_type
+        self.ad_data      = ad_data
+        self.rssi         = rssi
+        self.connectable  = connectable
+
+    def to_display_string(self):
+        address_type_string = ('P', 'R', 'PI', 'RI')[self.address_type]
+        address_color = colors.yellow if self.connectable else colors.red
+        if address_type_string.startswith('P'):
+            type_color = colors.green
+        else:
+            type_color = colors.cyan
+
+        name = self.ad_data.get(AdvertisingData.COMPLETE_LOCAL_NAME)
+        if name is None:
+            name = self.ad_data.get(AdvertisingData.SHORTENED_LOCAL_NAME)
+        if name:
+            # Convert to string
+            try:
+                name = name.decode()
+            except UnicodeDecodeError:
+                name = name.hex()
+        else:
+            name = ''
+
+        # RSSI bar
+        blocks = ['', '▏', '▎', '▍', '▌', '▋', '▊', '▉']
+        bar_width = (self.rssi - DISPLAY_MIN_RSSI) / (DISPLAY_MAX_RSSI - DISPLAY_MIN_RSSI)
+        bar_width = min(max(bar_width, 0), 1)
+        bar_ticks = int(bar_width * DEFAULT_RSSI_BAR_WIDTH * 8)
+        bar_blocks = ('█' * int(bar_ticks / 8)) + blocks[bar_ticks % 8]
+        bar_string = f'{self.rssi} {bar_blocks}'
+        bar_padding = ' ' * (DEFAULT_RSSI_BAR_WIDTH + 5 - len(bar_string))
+        return f'{address_color(str(self.address))} [{type_color(address_type_string)}] {bar_string} {bar_padding} {name}'
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+class LogHandler(logging.Handler):
+    def __init__(self, app):
+        super().__init__()
+        self.app = app
+
+    def emit(self, record):
+        message = self.format(record)
+        self.app.append_to_log(message)
+
+
+# -----------------------------------------------------------------------------
+# Main
+# -----------------------------------------------------------------------------
+@click.command()
+@click.option('--device-config', help='Device configuration file')
+@click.argument('transport')
+def main(device_config, transport):
+    # Ensure that the BUMBLE_USER_DIR directory exists
+    if not os.path.isdir(BUMBLE_USER_DIR):
+        os.mkdir(BUMBLE_USER_DIR)
+
+    # Create an instane of the app
+    app = ConsoleApp()
+
+    # Setup logging
+    # logging.basicConfig(level = 'FATAL')
+    # logging.basicConfig(level = 'DEBUG')
+    root_logger = logging.getLogger()
+    root_logger.addHandler(LogHandler(app))
+    root_logger.setLevel(logging.DEBUG)
+
+    # Run until the user exits
+    asyncio.run(app.run_async(device_config, transport))
+
+
+# -----------------------------------------------------------------------------
+if __name__ == "__main__":
+    main()
diff --git a/apps/controllers.py b/apps/controllers.py
new file mode 100644
index 0000000..8e5f70e
--- /dev/null
+++ b/apps/controllers.py
@@ -0,0 +1,63 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import sys
+import os
+
+from bumble.controller import Controller
+from bumble.link import LocalLink
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+async def async_main():
+    if len(sys.argv) != 3:
+        print('Usage: controllers.py <hci-transport-1> <hci-transport-2> [<hci-transport-3> ...]')
+        print('example: python controllers.py pty:ble1 pty:ble2')
+        return
+
+    # Create a loccal link to attach the controllers to
+    link = LocalLink()
+
+    # Create a transport and controller for all requested names
+    transports = []
+    controllers = []
+    for index, transport_name in enumerate(sys.argv[1:]):
+        transport = await open_transport_or_link(transport_name)
+        transports.append(transport)
+        controller = Controller(f'C{index}', host_source = transport.source, host_sink = transport.sink, link = link)
+        controllers.append(controller)
+
+    # Wait until the user interrupts
+    await asyncio.get_running_loop().create_future()
+
+    # Cleanup
+    for transport in transports:
+        transport.close()
+
+
+# -----------------------------------------------------------------------------
+def main():
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(async_main())
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/gatt_dump.py b/apps/gatt_dump.py
new file mode 100644
index 0000000..02c3641
--- /dev/null
+++ b/apps/gatt_dump.py
@@ -0,0 +1,108 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import os
+import logging
+import click
+from colors import color
+
+from bumble.core import ProtocolError, TimeoutError
+from bumble.device import Device, Peer
+from bumble.gatt import show_services
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+async def dump_gatt_db(peer, done):
+    # Discover all services
+    print(color('### Discovering Services and Characteristics', 'magenta'))
+    await peer.discover_services()
+    for service in peer.services:
+        await service.discover_characteristics()
+        for characteristic in service.characteristics:
+            await characteristic.discover_descriptors()
+
+    print(color('=== Services ===', 'yellow'))
+    show_services(peer.services)
+    print()
+
+    # Discover all attributes
+    print(color('=== All Attributes ===', 'yellow'))
+    attributes = await peer.discover_attributes()
+    for attribute in attributes:
+        print(attribute)
+        try:
+            value = await attribute.read_value()
+            print(color(f'{value.hex()}', 'green'))
+        except ProtocolError as error:
+            print(color(error, 'red'))
+        except TimeoutError:
+            print(color('read timeout', 'red'))
+
+    if done is not None:
+        done.set_result(None)
+
+
+# -----------------------------------------------------------------------------
+async def async_main(device_config, encrypt, transport, address_or_name):
+    async with await open_transport_or_link(transport) as (hci_source, hci_sink):
+
+        # Create a device
+        if device_config:
+            device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
+        else:
+            device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+        await device.power_on()
+
+        if address_or_name:
+            # Connect to the target peer
+            connection = await device.connect(address_or_name)
+
+            # Encrypt the connection if required
+            if encrypt:
+                await connection.encrypt()
+
+            await dump_gatt_db(Peer(connection), None)
+        else:
+            # Wait for a connection
+            done = asyncio.get_running_loop().create_future()
+            device.on('connection', lambda connection: asyncio.create_task(dump_gatt_db(Peer(connection), done)))
+            await device.start_advertising(auto_restart=True)
+
+            print(color('### Waiting for connection...', 'blue'))
+            await done
+
+
+# -----------------------------------------------------------------------------
+@click.command()
+@click.option('--device-config', help='Device configuration', type=click.Path())
+@click.option('--encrypt', help='Encrypt the connection', is_flag=True, default=False)
+@click.argument('transport')
+@click.argument('address-or-name', required=False)
+def main(device_config, encrypt, transport, address_or_name):
+    """
+    Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
+    wait for an incoming connection.
+    """
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(async_main(device_config, encrypt, transport, address_or_name))
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/gg_bridge.py b/apps/gg_bridge.py
new file mode 100644
index 0000000..d524913
--- /dev/null
+++ b/apps/gg_bridge.py
@@ -0,0 +1,211 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import os
+import logging
+import click
+from colors import color
+
+from bumble.device import Device, Peer
+from bumble.core import AdvertisingData
+from bumble.gatt import Service, Characteristic
+from bumble.utils import AsyncRunner
+from bumble.transport import open_transport_or_link
+from bumble.hci import HCI_Constant
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+GG_GATTLINK_SERVICE_UUID                          = 'ABBAFF00-E56A-484C-B832-8B17CF6CBFE8'
+GG_GATTLINK_RX_CHARACTERISTIC_UUID                = 'ABBAFF01-E56A-484C-B832-8B17CF6CBFE8'
+GG_GATTLINK_TX_CHARACTERISTIC_UUID                = 'ABBAFF02-E56A-484C-B832-8B17CF6CBFE8'
+GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID = 'ABBAFF03-E56A-484C-B832-8B17CF6CBFE8'
+
+GG_PREFERRED_MTU = 256
+
+
+# -----------------------------------------------------------------------------
+class GattlinkHubBridge(Device.Listener):
+    def __init__(self):
+        self.peer              = None
+        self.rx_socket         = None
+        self.tx_socket         = None
+        self.rx_characteristic = None
+        self.tx_characteristic = None
+
+    @AsyncRunner.run_in_task()
+    async def on_connection(self, connection):
+        print(f'=== Connected to {connection}')
+        self.peer = Peer(connection)
+
+        # Request a larger MTU than the default
+        server_mtu = await self.peer.request_mtu(GG_PREFERRED_MTU)
+        print(f'### Server MTU = {server_mtu}')
+
+        # Discover all services
+        print(color('=== Discovering services', 'yellow'))
+        await self.peer.discover_service(GG_GATTLINK_SERVICE_UUID)
+        print(color('=== Services discovered', 'yellow'), self.peer.services)
+        for service in self.peer.services:
+            print(service)
+        services = self.peer.get_services_by_uuid(GG_GATTLINK_SERVICE_UUID)
+        if not services:
+            print(color('!!! Gattlink service not found', 'red'))
+            return
+
+        # Use the first Gattlink (there should only be one anyway)
+        gattlink_service = services[0]
+
+        # Discover all the characteristics for the service
+        characteristics = await gattlink_service.discover_characteristics()
+        print(color('=== Characteristics discovered', 'yellow'))
+        for characteristic in characteristics:
+            if characteristic.uuid == GG_GATTLINK_RX_CHARACTERISTIC_UUID:
+                self.rx_characteristic = characteristic
+            elif characteristic.uuid == GG_GATTLINK_TX_CHARACTERISTIC_UUID:
+                self.tx_characteristic = characteristic
+        print('RX:', self.rx_characteristic)
+        print('TX:', self.tx_characteristic)
+
+        # Subscribe to TX
+        if self.tx_characteristic:
+            await self.peer.subscribe(self.tx_characteristic, self.on_tx_received)
+            print(color('=== Subscribed to Gattlink TX', 'yellow'))
+        else:
+            print(color('!!! Gattlink TX not found', 'red'))
+
+    def on_connection_failure(self, error):
+        print(color(f'!!! Connection failed: {error}'))
+
+    def on_disconnection(self, reason):
+        print(color(f'!!! Disconnected from {self.peer}, reason={HCI_Constant.error_name(reason)}', 'red'))
+        self.tx_characteristic = None
+        self.rx_characteristic = None
+        self.peer = None
+
+    # Called by the GATT client when a notification is received
+    def on_tx_received(self, value):
+        print(color('>>> TX:', 'magenta'), value.hex())
+        if self.tx_socket:
+            self.tx_socket.sendto(value)
+
+    # Called by asyncio when the UDP socket is created
+    def connection_made(self, transport):
+        pass
+
+    # Called by asyncio when a UDP datagram is received
+    def datagram_received(self, data, address):
+        print(color('<<< RX:', 'magenta'), data.hex())
+
+        # TODO: use a queue instead of creating a task everytime
+        if self.peer and self.rx_characteristic:
+            asyncio.create_task(self.peer.write_value(self.rx_characteristic, data))
+
+
+# -----------------------------------------------------------------------------
+class GattlinkNodeBridge(Device.Listener):
+    def __init__(self):
+        self.peer      = None
+        self.rx_socket = None
+        self.tx_socket = None
+
+    # Called by asyncio when the UDP socket is created
+    def connection_made(self, transport):
+        pass
+
+    # Called by asyncio when a UDP datagram is received
+    def datagram_received(self, data, address):
+        print(color('<<< RX:', 'magenta'), data.hex())
+
+        # TODO: use a queue instead of creating a task everytime
+        if self.peer and self.rx_characteristic:
+            asyncio.create_task(self.peer.write_value(self.rx_characteristic, data))
+
+
+# -----------------------------------------------------------------------------
+async def run(hci_transport, device_address, send_host, send_port, receive_host, receive_port):
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Instantiate a bridge object
+        bridge = GattlinkNodeBridge()
+
+        # Create a UDP to RX bridge (receive from UDP, send to RX)
+        loop = asyncio.get_running_loop()
+        await loop.create_datagram_endpoint(
+            lambda: bridge,
+            local_addr=(receive_host, receive_port)
+        )
+
+        # Create a UDP to TX bridge (receive from TX, send to UDP)
+        bridge.tx_socket, _ = await loop.create_datagram_endpoint(
+            lambda: asyncio.DatagramProtocol(),
+            remote_addr=(send_host, send_port)
+        )
+
+        # Create a device to manage the host, with a custom listener
+        device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+        device.listener = bridge
+        await device.power_on()
+
+        # Connect to the peer
+        # print(f'=== Connecting to {device_address}...')
+        # await device.connect(device_address)
+
+        # TODO move to class
+        gattlink_service = Service(
+            GG_GATTLINK_SERVICE_UUID,
+            [
+                Characteristic(
+                    GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID,
+                    Characteristic.READ,
+                    Characteristic.READABLE,
+                    bytes([193, 0])
+                )
+            ]
+        )
+        device.add_services([gattlink_service])
+        device.advertising_data = bytes(
+            AdvertisingData([
+                (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble GG', 'utf-8')),
+                (AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, bytes(reversed(bytes.fromhex('ABBAFF00E56A484CB8328B17CF6CBFE8'))))
+            ])
+        )
+        await device.start_advertising()
+
+        # Wait until the source terminates
+        await hci_source.wait_for_termination()
+
+
+@click.command()
+@click.argument('hci_transport')
+@click.argument('device_address')
+@click.option('-sh', '--send-host', type=str, default='127.0.0.1', help='UDP host to send to')
+@click.option('-sp', '--send-port', type=int, default=9001, help='UDP port to send to')
+@click.option('-rh', '--receive-host', type=str, default='127.0.0.1', help='UDP host to receive on')
+@click.option('-rp', '--receive-port', type=int, default=9000, help='UDP port to receive on')
+def main(hci_transport, device_address, send_host, send_port, receive_host, receive_port):
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(run(hci_transport, device_address, send_host, send_port, receive_host, receive_port))
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/hci_bridge.py b/apps/hci_bridge.py
new file mode 100644
index 0000000..0680f52
--- /dev/null
+++ b/apps/hci_bridge.py
@@ -0,0 +1,89 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import os
+import sys
+
+from bumble import hci, transport
+from bumble.bridge import HCI_Bridge
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Main
+# -----------------------------------------------------------------------------
+async def async_main():
+    if len(sys.argv) < 3:
+        print('Usage: hci_bridge.py <host-transport-spec> <controller-transport-spec> [command-short-circuit-list]')
+        print('example: python hci_bridge.py udp:0.0.0.0:9000,127.0.0.1:9001 serial:/dev/tty.usbmodem0006839912171,1000000 0x3f:0x0070,0x3f:0x0074,0x3f:0x0077,0x3f:0x0078')
+        return
+
+    print('>>> connecting to HCI...')
+    async with await transport.open_transport_or_link(sys.argv[1]) as (hci_host_source, hci_host_sink):
+        print('>>> connected')
+
+        print('>>> connecting to HCI...')
+        async with await transport.open_transport_or_link(sys.argv[2]) as (hci_controller_source, hci_controller_sink):
+            print('>>> connected')
+
+            command_short_circuits = []
+            if len(sys.argv) >= 4:
+                for op_code_str in sys.argv[3].split(','):
+                    if ':' in op_code_str:
+                        ogf, ocf = op_code_str.split(':')
+                        command_short_circuits.append(hci.hci_command_op_code(int(ogf, 16), int(ocf, 16)))
+                    else:
+                        command_short_circuits.append(int(op_code_str, 16))
+
+            def host_to_controller_filter(hci_packet):
+                if hci_packet.hci_packet_type == hci.HCI_COMMAND_PACKET and hci_packet.op_code in command_short_circuits:
+                    # Respond with a success response
+                    logger.debug('short-circuiting packet')
+                    response = hci.HCI_Command_Complete_Event(
+                        num_hci_command_packets = 1,
+                        command_opcode          = hci_packet.op_code,
+                        return_parameters       = bytes([hci.HCI_SUCCESS])
+                    )
+                    # Return a packet with 'respond to sender' set to True
+                    return (response.to_bytes(), True)
+
+            _ = HCI_Bridge(
+                hci_host_source,
+                hci_host_sink,
+                hci_controller_source,
+                hci_controller_sink,
+                host_to_controller_filter,
+                None
+            )
+            await asyncio.get_running_loop().create_future()
+
+
+# -----------------------------------------------------------------------------
+def main():
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(async_main())
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/link_relay/__init__.py b/apps/link_relay/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/apps/link_relay/__init__.py
diff --git a/apps/link_relay/link_relay.py b/apps/link_relay/link_relay.py
new file mode 100644
index 0000000..c979ea6
--- /dev/null
+++ b/apps/link_relay/link_relay.py
@@ -0,0 +1,276 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ----------------------------------------------------------------------------
+# Imports
+# ----------------------------------------------------------------------------
+import sys
+import websockets
+import logging
+import json
+import asyncio
+import argparse
+import uuid
+import os
+from urllib.parse import urlparse
+from colors import color
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# ----------------------------------------------------------------------------
+# Constants
+# ----------------------------------------------------------------------------
+DEFAULT_RELAY_PORT = 10723
+
+
+# ----------------------------------------------------------------------------
+# Utils
+# ----------------------------------------------------------------------------
+def error_to_json(error):
+    return json.dumps({'error': error})
+
+
+def error_to_result(error):
+    return f'result:{error_to_json(error)}'
+
+
+async def broadcast_message(message, connections):
+    # Send to all the connections
+    tasks = [connection.send_message(message) for connection in connections]
+    if tasks:
+        await asyncio.gather(*tasks)
+
+
+# ----------------------------------------------------------------------------
+# Connection class
+# ----------------------------------------------------------------------------
+class Connection:
+    """
+    A Connection represents a client connected to the relay over a websocket
+    """
+
+    def __init__(self, room, websocket):
+        self.room      = room
+        self.websocket = websocket
+        self.address   = str(uuid.uuid4())
+
+    async def send_message(self, message):
+        try:
+            logger.debug(color(f'->{self.address}: {message}', 'yellow'))
+            return await self.websocket.send(message)
+        except websockets.exceptions.WebSocketException as error:
+            logger.info(f'! client "{self}" disconnected: {error}')
+            await self.cleanup()
+
+    async def send_error(self, error):
+        return await self.send_message(f'result:{error_to_json(error)}')
+
+    async def receive_message(self):
+        try:
+            message = await self.websocket.recv()
+            logger.debug(color(f'<-{self.address}: {message}', 'blue'))
+            return message
+        except websockets.exceptions.WebSocketException as error:
+            logger.info(color(f'! client "{self}" disconnected: {error}', 'red'))
+            await self.cleanup()
+
+    async def cleanup(self):
+        if self.room:
+            await self.room.remove_connection(self)
+
+    def set_address(self, address):
+        logger.info(f'Connection address changed: {self.address} -> {address}')
+        self.address = address
+
+    def __str__(self):
+        return f'Connection(address="{self.address}", client={self.websocket.remote_address[0]}:{self.websocket.remote_address[1]})'
+
+
+# ----------------------------------------------------------------------------
+# Room class
+# ----------------------------------------------------------------------------
+class Room:
+    """
+    A Room is a collection of bridged connections
+    """
+
+    def __init__(self, relay, name):
+        self.relay       = relay
+        self.name        = name
+        self.observers   = []
+        self.connections = []
+
+    async def add_connection(self, connection):
+        logger.info(f'New participant in {self.name}: {connection}')
+        self.connections.append(connection)
+        await self.broadcast_message(connection, f'joined:{connection.address}')
+
+    async def remove_connection(self, connection):
+        if connection in self.connections:
+            self.connections.remove(connection)
+            await self.broadcast_message(connection, f'left:{connection.address}')
+
+    def find_connections_by_address(self, address):
+        return [c for c in self.connections if c.address == address]
+
+    async def bridge_connection(self, connection):
+        while True:
+            # Wait for a message
+            message = await connection.receive_message()
+
+            # Skip empty messages
+            if message is None:
+                return
+
+            # Parse the message to decide how to handle it
+            if message.startswith('@'):
+                # This is a targetted message
+                await self.on_targetted_message(connection, message)
+            elif message.startswith('/'):
+                # This is an RPC request
+                await self.on_rpc_request(connection, message)
+            else:
+                await connection.send_message(f'result:{error_to_json("error: invalid message")}')
+
+    async def broadcast_message(self, sender, message):
+        '''
+        Send to all connections in the room except back to the sender
+        '''
+        await broadcast_message(message, [c for c in self.connections if c != sender])
+
+    async def on_rpc_request(self, connection, message):
+        command, *params = message.split(' ', 1)
+        if handler := getattr(self, f'on_{command[1:].lower().replace("-","_")}_command', None):
+            try:
+                result = await handler(connection, params)
+            except Exception as error:
+                result = error_to_result(error)
+        else:
+            result = error_to_result('unknown command')
+
+        await connection.send_message(result or 'result:{}')
+
+    async def on_targetted_message(self, connection, message):
+        target, *payload = message.split(' ', 1)
+        if not payload:
+            return error_to_json('missing arguments')
+        payload = payload[0]
+        target = target[1:]
+
+        # Determine what targets to send to
+        if target == '*':
+            # Send to all connections in the room except the connection from which the message was received
+            connections = [c for c in self.connections if c != connection]
+        else:
+            connections = self.find_connections_by_address(target)
+            if not connections:
+                # Unicast with no recipient, let the sender know
+                await connection.send_message(f'unreachable:{target}')
+
+        # Send to targets
+        await broadcast_message(f'message:{connection.address}/{payload}', connections)
+
+    async def on_set_address_command(self, connection, params):
+        if not params:
+            return error_to_result('missing address')
+
+        current_address = connection.address
+        new_address = params[0]
+        connection.set_address(new_address)
+        await self.broadcast_message(connection, f'address-changed:from={current_address},to={new_address}')
+
+
+# ----------------------------------------------------------------------------
+class Relay:
+    """
+    A relay accepts connections with the following url: ws://<hostname>/<room>.
+    Participants in a room can communicate with each other
+    """
+
+    def __init__(self, port):
+        self.port = port
+        self.rooms = {}
+        self.observers = []
+
+    def start(self):
+        logger.info(f'Starting Relay on port {self.port}')
+
+        return websockets.serve(self.serve, '0.0.0.0', self.port, ping_interval=None)
+
+    async def serve_as_controller(connection):
+        pass
+
+    async def serve(self, websocket, path):
+        logger.debug(f'New connection with path {path}')
+
+        # Parse the path
+        parsed = urlparse(path)
+
+        # Check if this is a controller client
+        if parsed.path == '/':
+            return await self.serve_as_controller(Connection('', websocket))
+
+        # Find or create a room for this connection
+        room_name = parsed.path[1:].split('/')[0]
+        if room_name not in self.rooms:
+            self.rooms[room_name] = Room(self, room_name)
+        room = self.rooms[room_name]
+
+        # Add the connection to the room
+        connection = Connection(room, websocket)
+        await room.add_connection(connection)
+
+        # Bridge until the connection is closed
+        await room.bridge_connection(connection)
+
+
+# ----------------------------------------------------------------------------
+def main():
+    # Check the Python version
+    if sys.version_info < (3, 6, 1):
+        print('ERROR: Python 3.6.1 or higher is required')
+        sys.exit(1)
+
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+
+    # Parse arguments
+    arg_parser = argparse.ArgumentParser(description='Bumble Link Relay')
+    arg_parser.add_argument('--log-level', default='INFO', help='logger level')
+    arg_parser.add_argument('--log-config', help='logger config file (YAML)')
+    arg_parser.add_argument('--port',
+                            type = int,
+                            default = DEFAULT_RELAY_PORT,
+                            help = 'Port to listen on')
+    args = arg_parser.parse_args()
+
+    # Setup logger
+    if args.log_config:
+        from logging import config
+        config.fileConfig(args.log_config)
+    else:
+        logging.basicConfig(level = getattr(logging, args.log_level.upper()))
+
+    # Start a relay
+    relay = Relay(args.port)
+    asyncio.get_event_loop().run_until_complete(relay.start())
+    asyncio.get_event_loop().run_forever()
+
+
+# ----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/link_relay/logging.yml b/apps/link_relay/logging.yml
new file mode 100644
index 0000000..0f6df12
--- /dev/null
+++ b/apps/link_relay/logging.yml
@@ -0,0 +1,21 @@
+[loggers]
+keys=root
+
+[handlers]
+keys=stream_handler
+
+[formatters]
+keys=formatter
+
+[logger_root]
+level=DEBUG
+handlers=stream_handler
+
+[handler_stream_handler]
+class=StreamHandler
+level=DEBUG
+formatter=formatter
+args=(sys.stderr,)
+
+[formatter_formatter]
+format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
diff --git a/apps/pair.py b/apps/pair.py
new file mode 100644
index 0000000..7f89629
--- /dev/null
+++ b/apps/pair.py
@@ -0,0 +1,341 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import os
+import logging
+import click
+import aioconsole
+from colors import color
+
+from bumble.device import Device, Peer
+from bumble.transport import open_transport_or_link
+from bumble.smp import PairingDelegate, PairingConfig
+from bumble.smp import error_name as smp_error_name
+from bumble.keys import JsonKeyStore
+from bumble.core import ProtocolError
+from bumble.gatt import (
+    GATT_DEVICE_NAME_CHARACTERISTIC,
+    GATT_GENERIC_ACCESS_SERVICE,
+    Service,
+    Characteristic,
+    CharacteristicValue
+)
+from bumble.att import (
+    ATT_Error,
+    ATT_INSUFFICIENT_AUTHENTICATION_ERROR,
+    ATT_INSUFFICIENT_ENCRYPTION_ERROR
+)
+
+
+# -----------------------------------------------------------------------------
+class Delegate(PairingDelegate):
+    def __init__(self, mode, connection, capability_string, prompt):
+        super().__init__({
+            'keyboard':         PairingDelegate.KEYBOARD_INPUT_ONLY,
+            'display':          PairingDelegate.DISPLAY_OUTPUT_ONLY,
+            'display+keyboard': PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT,
+            'display+yes/no':   PairingDelegate.DISPLAY_OUTPUT_AND_YES_NO_INPUT,
+            'none':             PairingDelegate.NO_OUTPUT_NO_INPUT
+        }[capability_string.lower()])
+
+        self.mode      = mode
+        self.peer      = Peer(connection)
+        self.peer_name = None
+        self.prompt    = prompt
+
+    async def update_peer_name(self):
+        if self.peer_name is not None:
+            # We already asked the peer
+            return
+
+        # Try to get the peer's name
+        if self.peer:
+            peer_name = await get_peer_name(self.peer, self.mode)
+            self.peer_name = f'{peer_name or ""} [{self.peer.connection.peer_address}]'
+        else:
+            self.peer_name = '[?]'
+
+    async def accept(self):
+        if self.prompt:
+            await self.update_peer_name()
+
+            # Wait a bit to allow some of the log lines to print before we prompt
+            await asyncio.sleep(1)
+
+            # Prompt for acceptance
+            print(color('###-----------------------------------', 'yellow'))
+            print(color(f'### Pairing request from {self.peer_name}', 'yellow'))
+            print(color('###-----------------------------------', 'yellow'))
+            while True:
+                response = await aioconsole.ainput(color('>>> Accept? ', 'yellow'))
+                response = response.lower().strip()
+                if response == 'yes':
+                    return True
+                elif response == 'no':
+                    return False
+        else:
+            # Accept silently
+            return True
+
+    async def compare_numbers(self, number, digits):
+        await self.update_peer_name()
+
+        # Wait a bit to allow some of the log lines to print before we prompt
+        await asyncio.sleep(1)
+
+        # Prompt for a numeric comparison
+        print(color('###-----------------------------------', 'yellow'))
+        print(color(f'### Pairing with {self.peer_name}', 'yellow'))
+        print(color('###-----------------------------------', 'yellow'))
+        while True:
+            response = await aioconsole.ainput(color(f'>>> Does the other device display {number:0{digits}}? ', 'yellow'))
+            response = response.lower().strip()
+            if response == 'yes':
+                return True
+            elif response == 'no':
+                return False
+
+    async def get_number(self):
+        await self.update_peer_name()
+
+        # Wait a bit to allow some of the log lines to print before we prompt
+        await asyncio.sleep(1)
+
+        # Prompt for a PIN
+        while True:
+            try:
+                print(color('###-----------------------------------', 'yellow'))
+                print(color(f'### Pairing with {self.peer_name}', 'yellow'))
+                print(color('###-----------------------------------', 'yellow'))
+                return int(await aioconsole.ainput(color('>>> Enter PIN: ', 'yellow')))
+            except ValueError:
+                pass
+
+    async def display_number(self, number, digits):
+        await self.update_peer_name()
+
+        # Wait a bit to allow some of the log lines to print before we prompt
+        await asyncio.sleep(1)
+
+        # Display a PIN code
+        print(color('###-----------------------------------', 'yellow'))
+        print(color(f'### Pairing with {self.peer_name}', 'yellow'))
+        print(color(f'### PIN: {number:0{digits}}', 'yellow'))
+        print(color('###-----------------------------------', 'yellow'))
+
+
+# -----------------------------------------------------------------------------
+async def get_peer_name(peer, mode):
+    if mode == 'classic':
+        return await peer.request_name()
+    else:
+        # Try to get the peer name from GATT
+        services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE)
+        if not services:
+            return None
+
+        values = await peer.read_characteristics_by_uuid(GATT_DEVICE_NAME_CHARACTERISTIC, services[0])
+        if values:
+            return values[0].decode('utf-8')
+
+
+# -----------------------------------------------------------------------------
+AUTHENTICATION_ERROR_RETURNED = [False, False]
+
+
+def read_with_error(connection):
+    if not connection.is_encrypted:
+        raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR)
+
+    if AUTHENTICATION_ERROR_RETURNED[0]:
+        return bytes([1])
+    else:
+        AUTHENTICATION_ERROR_RETURNED[0] = True
+        raise ATT_Error(ATT_INSUFFICIENT_AUTHENTICATION_ERROR)
+
+
+def write_with_error(connection, value):
+    if not connection.is_encrypted:
+        raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR)
+
+    if not AUTHENTICATION_ERROR_RETURNED[1]:
+        AUTHENTICATION_ERROR_RETURNED[1] = True
+        raise ATT_Error(ATT_INSUFFICIENT_AUTHENTICATION_ERROR)
+
+
+# -----------------------------------------------------------------------------
+def on_connection(connection, request):
+    print(color(f'<<< Connection: {connection}', 'green'))
+
+    # Listen for pairing events
+    connection.on('pairing_start',   on_pairing_start)
+    connection.on('pairing',         on_pairing)
+    connection.on('pairing_failure', on_pairing_failure)
+
+    # Listen for encryption changes
+    connection.on(
+        'connection_encryption_change',
+        lambda: on_connection_encryption_change(connection)
+    )
+
+    # Request pairing if needed
+    if request:
+        print(color('>>> Requesting pairing', 'green'))
+        connection.request_pairing()
+
+
+# -----------------------------------------------------------------------------
+def on_connection_encryption_change(connection):
+    print(color('@@@-----------------------------------', 'blue'))
+    print(color(f'@@@ Connection is {"" if connection.is_encrypted else "not"}encrypted', 'blue'))
+    print(color('@@@-----------------------------------', 'blue'))
+
+
+# -----------------------------------------------------------------------------
+def on_pairing_start():
+    print(color('***-----------------------------------', 'magenta'))
+    print(color('*** Pairing starting', 'magenta'))
+    print(color('***-----------------------------------', 'magenta'))
+
+
+# -----------------------------------------------------------------------------
+def on_pairing(keys):
+    print(color('***-----------------------------------', 'cyan'))
+    print(color('*** Paired!', 'cyan'))
+    keys.print(prefix=color('*** ', 'cyan'))
+    print(color('***-----------------------------------', 'cyan'))
+
+
+# -----------------------------------------------------------------------------
+def on_pairing_failure(reason):
+    print(color('***-----------------------------------', 'red'))
+    print(color(f'*** Pairing failed: {smp_error_name(reason)}', 'red'))
+    print(color('***-----------------------------------', 'red'))
+
+
+# -----------------------------------------------------------------------------
+async def pair(
+    mode,
+    sc,
+    mitm,
+    bond,
+    io,
+    prompt,
+    request,
+    print_keys,
+    keystore_file,
+    device_config,
+    hci_transport,
+    address_or_name
+):
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device to manage the host
+        device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
+
+        # Set a custom keystore if specified on the command line
+        if keystore_file:
+            device.keystore = JsonKeyStore(namespace=None, filename=keystore_file)
+
+        # Print the existing keys before pairing
+        if print_keys and device.keystore:
+            print(color('@@@-----------------------------------', 'blue'))
+            print(color('@@@ Pairing Keys:', 'blue'))
+            await device.keystore.print(prefix=color('@@@ ', 'blue'))
+            print(color('@@@-----------------------------------', 'blue'))
+
+        # Expose a GATT characteristic that can be used to trigger pairing by
+        # responding with an authentication error when read
+        if mode == 'le':
+            device.add_service(
+                Service(
+                    '50DB505C-8AC4-4738-8448-3B1D9CC09CC5',
+                    [
+                        Characteristic(
+                            '552957FB-CF1F-4A31-9535-E78847E1A714',
+                            Characteristic.READ | Characteristic.WRITE,
+                            Characteristic.READABLE | Characteristic.WRITEABLE,
+                            CharacteristicValue(read=read_with_error, write=write_with_error)
+                        )
+                    ]
+                )
+            )
+
+        # Select LE or Classic
+        if mode == 'classic':
+            device.classic_enabled = True
+            device.le_enabled = False
+
+        # Get things going
+        await device.power_on()
+
+        # Set up a pairing config factory
+        device.pairing_config_factory = lambda connection: PairingConfig(
+            sc,
+            mitm,
+            bond,
+            Delegate(mode, connection, io, prompt)
+        )
+
+        # Connect to a peer or wait for a connection
+        device.on('connection', lambda connection: on_connection(connection, request))
+        if address_or_name is not None:
+            print(color(f'=== Connecting to {address_or_name}...', 'green'))
+            connection = await device.connect(address_or_name)
+
+            if not request:
+                try:
+                    if mode == 'le':
+                        await connection.pair()
+                    else:
+                        await connection.authenticate()
+                    return
+                except ProtocolError as error:
+                    print(color(f'Pairing failed: {error}', 'red'))
+                    return
+        else:
+            # Advertise so that peers can find us and connect
+            await device.start_advertising(auto_restart=True)
+
+        await hci_source.wait_for_termination()
+
+
+# -----------------------------------------------------------------------------
+@click.command()
+@click.option('--mode', type=click.Choice(['le', 'classic']), default='le', show_default=True)
+@click.option('--sc', type=bool, default=True, help='Use the Secure Connections protocol', show_default=True)
+@click.option('--mitm', type=bool, default=True, help='Request MITM protection', show_default=True)
+@click.option('--bond', type=bool, default=True, help='Enable bonding', show_default=True)
+@click.option('--io', type=click.Choice(['keyboard', 'display', 'display+keyboard', 'display+yes/no', 'none']), default='display+keyboard', show_default=True)
+@click.option('--prompt', is_flag=True, help='Prompt to accept/reject pairing request')
+@click.option('--request', is_flag=True, help='Request that the connecting peer initiate pairing')
+@click.option('--print-keys', is_flag=True, help='Print the bond keys before pairing')
+@click.option('--keystore-file', help='File in which to store the pairing keys')
+@click.argument('device-config')
+@click.argument('hci_transport')
+@click.argument('address-or-name', required=False)
+def main(mode, sc, mitm, bond, io, prompt, request, print_keys, keystore_file, device_config, hci_transport, address_or_name):
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(pair(mode, sc, mitm, bond, io, prompt, request, print_keys, keystore_file, device_config, hci_transport, address_or_name))
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/scan.py b/apps/scan.py
new file mode 100644
index 0000000..045cb57
--- /dev/null
+++ b/apps/scan.py
@@ -0,0 +1,157 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import os
+import logging
+import click
+from colors import color
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.keys import JsonKeyStore
+from bumble.smp import AddressResolver
+from bumble.hci import HCI_LE_Advertising_Report_Event
+from bumble.core import AdvertisingData
+
+
+# -----------------------------------------------------------------------------
+def make_rssi_bar(rssi):
+    DISPLAY_MIN_RSSI       = -105
+    DISPLAY_MAX_RSSI       = -30
+    DEFAULT_RSSI_BAR_WIDTH = 30
+
+    blocks = ['', '▏', '▎', '▍', '▌', '▋', '▊', '▉']
+    bar_width = (rssi - DISPLAY_MIN_RSSI) / (DISPLAY_MAX_RSSI - DISPLAY_MIN_RSSI)
+    bar_width = min(max(bar_width, 0), 1)
+    bar_ticks = int(bar_width * DEFAULT_RSSI_BAR_WIDTH * 8)
+    return ('█' * int(bar_ticks / 8)) + blocks[bar_ticks % 8]
+
+
+# -----------------------------------------------------------------------------
+class AdvertisementPrinter:
+    def __init__(self, min_rssi, resolver):
+        self.min_rssi = min_rssi
+        self.resolver = resolver
+
+    def print_advertisement(self, address, address_color, ad_data, rssi):
+        if self.min_rssi is not None and rssi < self.min_rssi:
+            return
+
+        address_qualifier = ''
+        resolution_qualifier = ''
+        if self.resolver and address.is_resolvable:
+            resolved = self.resolver.resolve(address)
+            if resolved is not None:
+                resolution_qualifier = f'(resolved from {address})'
+                address = resolved
+
+        address_type_string = ('PUBLIC', 'RANDOM', 'PUBLIC_ID', 'RANDOM_ID')[address.address_type]
+        if address.is_public:
+            type_color = 'cyan'
+        else:
+            if address.is_static:
+                type_color = 'green'
+                address_qualifier = '(static)'
+            elif address.is_resolvable:
+                type_color = 'magenta'
+                address_qualifier = '(resolvable)'
+            else:
+                type_color = 'blue'
+                address_qualifier = '(non-resolvable)'
+
+        rssi_bar = make_rssi_bar(rssi)
+        separator = '\n  '
+        print(f'>>> {color(address, address_color)} [{color(address_type_string, type_color)}]{address_qualifier}{resolution_qualifier}:{separator}RSSI:{rssi:4} {rssi_bar}{separator}{ad_data.to_string(separator)}\n')
+
+    def on_advertisement(self, address, ad_data, rssi, connectable):
+        address_color = 'yellow' if connectable else 'red'
+        self.print_advertisement(address, address_color, ad_data, rssi)
+
+    def on_advertising_report(self, address, ad_data, rssi, event_type):
+        print(f'{color("EVENT", "green")}: {HCI_LE_Advertising_Report_Event.event_type_name(event_type)}')
+        ad_data = AdvertisingData.from_bytes(ad_data)
+        self.print_advertisement(address, 'yellow', ad_data, rssi)
+
+
+# -----------------------------------------------------------------------------
+async def scan(
+    min_rssi,
+    passive,
+    scan_interval,
+    scan_window,
+    filter_duplicates,
+    raw,
+    keystore_file,
+    device_config,
+    transport
+):
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(transport) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        if device_config:
+            device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
+        else:
+            device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+
+        if keystore_file:
+            keystore = JsonKeyStore(namespace=None, filename=keystore_file)
+            device.keystore = keystore
+        else:
+            resolver = None
+
+        if device.keystore:
+            resolving_keys = await device.keystore.get_resolving_keys()
+            resolver = AddressResolver(resolving_keys)
+
+        printer = AdvertisementPrinter(min_rssi, resolver)
+        if raw:
+            device.host.on('advertising_report', printer.on_advertising_report)
+        else:
+            device.on('advertisement', printer.on_advertisement)
+
+        await device.power_on()
+        await device.start_scanning(
+            active=(not passive),
+            scan_interval=scan_interval,
+            scan_window=scan_window,
+            filter_duplicates=filter_duplicates
+        )
+
+        await hci_source.wait_for_termination()
+
+
+# -----------------------------------------------------------------------------
+@click.command()
+@click.option('--min-rssi', type=int, help='Minimum RSSI value')
+@click.option('--passive', is_flag=True, default=False, help='Perform passive scanning')
+@click.option('--scan-interval', type=int, default=60, help='Scan interval')
+@click.option('--scan-window', type=int, default=60, help='Scan window')
+@click.option('--filter-duplicates', type=bool, default=True, help='Filter duplicates at the controller level')
+@click.option('--raw', is_flag=True, default=False, help='Listen for raw advertising reports instead of processed ones')
+@click.option('--keystore-file', help='Keystore file to use when resolving addresses')
+@click.option('--device-config', help='Device config file for the scanning device')
+@click.argument('transport')
+def main(min_rssi, passive, scan_interval, scan_window, filter_duplicates, raw, keystore_file, device_config, transport):
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
+    asyncio.run(scan(min_rssi, passive, scan_interval, scan_window, filter_duplicates, raw, keystore_file, device_config, transport))
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/apps/show.py b/apps/show.py
new file mode 100644
index 0000000..bba6c66
--- /dev/null
+++ b/apps/show.py
@@ -0,0 +1,120 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import struct
+import click
+from colors import color
+
+from bumble import hci
+from bumble.transport.common import PacketReader
+from bumble.helpers import PacketTracer
+
+
+# -----------------------------------------------------------------------------
+class SnoopPacketReader:
+    '''
+    Reader that reads HCI packets from a "snoop" file (based on RFC 1761, but not exactly the same...)
+    '''
+
+    DATALINK_H1   = 1001
+    DATALINK_H4   = 1002
+    DATALINK_BSCP = 1003
+    DATALINK_H5   = 1004
+
+    def __init__(self, source):
+        self.source = source
+
+        # Read the header
+        identification_pattern = source.read(8)
+        if identification_pattern.hex().lower() != '6274736e6f6f7000':
+            raise ValueError('not a valid snoop file, unexpected identification pattern')
+        (self.version_number, self.data_link_type) = struct.unpack('>II', source.read(8))
+        if self.data_link_type != self.DATALINK_H4 and self.data_link_type != self.DATALINK_H1:
+            raise ValueError(f'datalink type {self.data_link_type} not supported')
+
+    def next_packet(self):
+        # Read the record header
+        header = self.source.read(24)
+        if len(header) < 24:
+            return (0, None)
+        (
+            original_length,
+            included_length,
+            packet_flags,
+            cumulative_drops,
+            timestamp_seconds,
+            timestamp_microsecond
+        ) = struct.unpack('>IIIIII', header)
+
+        # Abort on truncated packets
+        if original_length != included_length:
+            return (0, None)
+
+        if self.data_link_type == self.DATALINK_H1:
+            # The packet is un-encapsulated, look at the flags to figure out its type
+            if packet_flags & 1:
+                # Controller -> Host
+                if packet_flags & 2:
+                    packet_type = hci.HCI_EVENT_PACKET
+                else:
+                    packet_type = hci.HCI_ACL_DATA_PACKET
+            else:
+                # Host -> Controller
+                if packet_flags & 2:
+                    packet_type = hci.HCI_COMMAND_PACKET
+                else:
+                    packet_type = hci.HCI_ACL_DATA_PACKET
+
+            return (packet_flags & 1, bytes([packet_type]) + self.source.read(included_length))
+        else:
+            return (packet_flags & 1, self.source.read(included_length))
+
+
+# -----------------------------------------------------------------------------
+# Main
+# -----------------------------------------------------------------------------
+@click.command()
+@click.option('--format', type=click.Choice(['h4', 'snoop']), default='h4', help='Format of the input file')
+@click.argument('filename')
+def show(format, filename):
+    input = open(filename, 'rb')
+    if format == 'h4':
+        packet_reader = PacketReader(input)
+
+        def read_next_packet():
+            (0, packet_reader.next_packet())
+    else:
+        packet_reader = SnoopPacketReader(input)
+        read_next_packet = packet_reader.next_packet
+
+    tracer = PacketTracer(emit_message=print)
+
+    while True:
+        try:
+            (direction, packet) = read_next_packet()
+            if packet is None:
+                break
+            tracer.trace(hci.HCI_Packet.from_bytes(packet), direction)
+
+        except Exception as error:
+            print(color(f'!!! {error}', 'red'))
+            pass
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    show()
diff --git a/apps/unbond.py b/apps/unbond.py
new file mode 100644
index 0000000..cf1877c
--- /dev/null
+++ b/apps/unbond.py
@@ -0,0 +1,63 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import os
+import logging
+import click
+
+from bumble.device import Device
+from bumble.keys import JsonKeyStore
+
+
+# -----------------------------------------------------------------------------
+async def unbond(keystore_file, device_config, address):
+    # Create a device to manage the host
+    device = Device.from_config_file(device_config)
+
+    # Get all entries in the keystore
+    if keystore_file:
+        keystore = JsonKeyStore(None, keystore_file)
+    else:
+        keystore = device.keystore
+
+    if keystore is None:
+        print('no keystore')
+        return
+
+    if address is None:
+        await keystore.print()
+    else:
+        try:
+            await keystore.delete(address)
+        except KeyError:
+            print('!!! pairing not found')
+
+
+# -----------------------------------------------------------------------------
+@click.command()
+@click.option('--keystore-file', help='File in which to store the pairing keys')
+@click.argument('device-config')
+@click.argument('address', required=False)
+def main(keystore_file, device_config, address):
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(unbond(keystore_file, device_config, address))
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    main()
diff --git a/bumble/__init__.py b/bumble/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bumble/__init__.py
diff --git a/bumble/a2dp.py b/bumble/a2dp.py
new file mode 100644
index 0000000..03c3fd2
--- /dev/null
+++ b/bumble/a2dp.py
@@ -0,0 +1,554 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import struct
+import bitstruct
+import logging
+from collections import namedtuple
+from colors import color
+
+from .company_ids import COMPANY_IDENTIFIERS
+from .sdp import (
+    DataElement,
+    ServiceAttribute,
+    SDP_PUBLIC_BROWSE_ROOT,
+    SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
+    SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+)
+from .core import (
+    BT_L2CAP_PROTOCOL_ID,
+    BT_AUDIO_SOURCE_SERVICE,
+    BT_AUDIO_SINK_SERVICE,
+    BT_AVDTP_PROTOCOL_ID,
+    BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
+    name_or_number
+)
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+
+A2DP_SBC_CODEC_TYPE            = 0x00
+A2DP_MPEG_1_2_AUDIO_CODEC_TYPE = 0x01
+A2DP_MPEG_2_4_AAC_CODEC_TYPE   = 0x02
+A2DP_ATRAC_FAMILY_CODEC_TYPE   = 0x03
+A2DP_NON_A2DP_CODEC_TYPE       = 0xFF
+
+A2DP_CODEC_TYPE_NAMES = {
+    A2DP_SBC_CODEC_TYPE:            'A2DP_SBC_CODEC_TYPE',
+    A2DP_MPEG_1_2_AUDIO_CODEC_TYPE: 'A2DP_MPEG_1_2_AUDIO_CODEC_TYPE',
+    A2DP_MPEG_2_4_AAC_CODEC_TYPE:   'A2DP_MPEG_2_4_AAC_CODEC_TYPE',
+    A2DP_ATRAC_FAMILY_CODEC_TYPE:   'A2DP_ATRAC_FAMILY_CODEC_TYPE',
+    A2DP_NON_A2DP_CODEC_TYPE:       'A2DP_NON_A2DP_CODEC_TYPE'
+}
+
+
+SBC_SYNC_WORD = 0x9C
+
+SBC_SAMPLING_FREQUENCIES = [
+    16000,
+    22050,
+    44100,
+    48000
+]
+
+SBC_MONO_CHANNEL_MODE         = 0x00
+SBC_DUAL_CHANNEL_MODE         = 0x01
+SBC_STEREO_CHANNEL_MODE       = 0x02
+SBC_JOINT_STEREO_CHANNEL_MODE = 0x03
+
+SBC_CHANNEL_MODE_NAMES = {
+    SBC_MONO_CHANNEL_MODE:         'SBC_MONO_CHANNEL_MODE',
+    SBC_DUAL_CHANNEL_MODE:         'SBC_DUAL_CHANNEL_MODE',
+    SBC_STEREO_CHANNEL_MODE:       'SBC_STEREO_CHANNEL_MODE',
+    SBC_JOINT_STEREO_CHANNEL_MODE: 'SBC_JOINT_STEREO_CHANNEL_MODE'
+}
+
+SBC_BLOCK_LENGTHS = [4, 8, 12, 16]
+
+SBC_SUBBANDS = [4, 8]
+
+SBC_SNR_ALLOCATION_METHOD      = 0x00
+SBC_LOUDNESS_ALLOCATION_METHOD = 0x01
+
+SBC_ALLOCATION_METHOD_NAMES = {
+    SBC_SNR_ALLOCATION_METHOD:      'SBC_SNR_ALLOCATION_METHOD',
+    SBC_LOUDNESS_ALLOCATION_METHOD: 'SBC_LOUDNESS_ALLOCATION_METHOD'
+}
+
+MPEG_2_4_AAC_SAMPLING_FREQUENCIES = [
+    8000,
+    11025,
+    12000,
+    16000,
+    22050,
+    24000,
+    32000,
+    44100,
+    48000,
+    64000,
+    88200,
+    96000
+]
+
+MPEG_2_AAC_LC_OBJECT_TYPE       = 0x00
+MPEG_4_AAC_LC_OBJECT_TYPE       = 0x01
+MPEG_4_AAC_LTP_OBJECT_TYPE      = 0x02
+MPEG_4_AAC_SCALABLE_OBJECT_TYPE = 0x03
+
+MPEG_2_4_OBJECT_TYPE_NAMES = {
+    MPEG_2_AAC_LC_OBJECT_TYPE:       'MPEG_2_AAC_LC_OBJECT_TYPE',
+    MPEG_4_AAC_LC_OBJECT_TYPE:       'MPEG_4_AAC_LC_OBJECT_TYPE',
+    MPEG_4_AAC_LTP_OBJECT_TYPE:      'MPEG_4_AAC_LTP_OBJECT_TYPE',
+    MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 'MPEG_4_AAC_SCALABLE_OBJECT_TYPE'
+}
+
+
+# -----------------------------------------------------------------------------
+def flags_to_list(flags, values):
+    result = []
+    for i in range(len(values)):
+        if flags & (1 << (len(values) - i - 1)):
+            result.append(values[i])
+    return result
+
+
+# -----------------------------------------------------------------------------
+def make_audio_source_service_sdp_records(service_record_handle, version=(1, 3)):
+    from .avdtp import AVDTP_PSM
+    version_int = version[0] << 8 | version[1]
+    return [
+        ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(service_record_handle)),
+        ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)
+        ])),
+        ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(BT_AUDIO_SOURCE_SERVICE)
+        ])),
+        ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.sequence([
+                DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
+                DataElement.unsigned_integer_16(AVDTP_PSM)
+            ]),
+            DataElement.sequence([
+                DataElement.uuid(BT_AVDTP_PROTOCOL_ID),
+                DataElement.unsigned_integer_16(version_int)
+            ])
+        ])),
+        ServiceAttribute(SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE),
+            DataElement.unsigned_integer_16(version_int)
+        ])),
+    ]
+
+
+# -----------------------------------------------------------------------------
+def make_audio_sink_service_sdp_records(service_record_handle, version=(1, 3)):
+    from .avdtp import AVDTP_PSM
+    version_int = version[0] << 8 | version[1]
+    return [
+        ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(service_record_handle)),
+        ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)
+        ])),
+        ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(BT_AUDIO_SINK_SERVICE)
+        ])),
+        ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.sequence([
+                DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
+                DataElement.unsigned_integer_16(AVDTP_PSM)
+            ]),
+            DataElement.sequence([
+                DataElement.uuid(BT_AVDTP_PROTOCOL_ID),
+                DataElement.unsigned_integer_16(version_int)
+            ])
+        ])),
+        ServiceAttribute(SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE),
+            DataElement.unsigned_integer_16(version_int)
+        ])),
+    ]
+
+
+# -----------------------------------------------------------------------------
+class SbcMediaCodecInformation(
+    namedtuple(
+        'SbcMediaCodecInformation',
+        [
+            'sampling_frequency',
+            'channel_mode',
+            'block_length',
+            'subbands',
+            'allocation_method',
+            'minimum_bitpool_value',
+            'maximum_bitpool_value'
+        ]
+    )
+):
+    '''
+    A2DP spec - 4.3.2 Codec Specific Information Elements
+    '''
+
+    BIT_FIELDS = 'u4u4u4u2u2u8u8'
+    SAMPLING_FREQUENCY_BITS = {
+        16000: 1 << 3,
+        32000: 1 << 2,
+        44100: 1 << 1,
+        48000: 1
+    }
+    CHANNEL_MODE_BITS = {
+        SBC_MONO_CHANNEL_MODE:         1 << 3,
+        SBC_DUAL_CHANNEL_MODE:         1 << 2,
+        SBC_STEREO_CHANNEL_MODE:       1 << 1,
+        SBC_JOINT_STEREO_CHANNEL_MODE: 1
+    }
+    BLOCK_LENGTH_BITS = {
+        4:  1 << 3,
+        8:  1 << 2,
+        12: 1 << 1,
+        16: 1
+    }
+    SUBBANDS_BITS = {
+        4: 1 << 1,
+        8: 1
+    }
+    ALLOCATION_METHOD_BITS = {
+        SBC_SNR_ALLOCATION_METHOD:      1 << 1,
+        SBC_LOUDNESS_ALLOCATION_METHOD: 1
+    }
+
+    @staticmethod
+    def from_bytes(data):
+        return SbcMediaCodecInformation(*bitstruct.unpack(SbcMediaCodecInformation.BIT_FIELDS, data))
+
+    @classmethod
+    def from_discrete_values(
+        cls,
+        sampling_frequency,
+        channel_mode,
+        block_length,
+        subbands,
+        allocation_method,
+        minimum_bitpool_value,
+        maximum_bitpool_value
+    ):
+        return SbcMediaCodecInformation(
+            sampling_frequency    = cls.SAMPLING_FREQUENCY_BITS[sampling_frequency],
+            channel_mode          = cls.CHANNEL_MODE_BITS[channel_mode],
+            block_length          = cls.BLOCK_LENGTH_BITS[block_length],
+            subbands              = cls.SUBBANDS_BITS[subbands],
+            allocation_method     = cls.ALLOCATION_METHOD_BITS[allocation_method],
+            minimum_bitpool_value = minimum_bitpool_value,
+            maximum_bitpool_value = maximum_bitpool_value
+        )
+
+    @classmethod
+    def from_lists(
+        cls,
+        sampling_frequencies,
+        channel_modes,
+        block_lengths,
+        subbands,
+        allocation_methods,
+        minimum_bitpool_value,
+        maximum_bitpool_value
+    ):
+        return SbcMediaCodecInformation(
+            sampling_frequency    = sum(cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies),
+            channel_mode          = sum(cls.CHANNEL_MODE_BITS[x] for x in channel_modes),
+            block_length          = sum(cls.BLOCK_LENGTH_BITS[x] for x in block_lengths),
+            subbands              = sum(cls.SUBBANDS_BITS[x] for x in subbands),
+            allocation_method     = sum(cls.ALLOCATION_METHOD_BITS[x] for x in allocation_methods),
+            minimum_bitpool_value = minimum_bitpool_value,
+            maximum_bitpool_value = maximum_bitpool_value
+        )
+
+    def __bytes__(self):
+        return bitstruct.pack(self.BIT_FIELDS, *self)
+
+    def __str__(self):
+        channel_modes = ['MONO', 'DUAL_CHANNEL', 'STEREO', 'JOINT_STEREO']
+        allocation_methods = ['SNR', 'Loudness']
+        return '\n'.join([
+            'SbcMediaCodecInformation(',
+            f'  sampling_frequency:    {",".join([str(x) for x in flags_to_list(self.sampling_frequency, SBC_SAMPLING_FREQUENCIES)])}',
+            f'  channel_mode:          {",".join([str(x) for x in flags_to_list(self.channel_mode, channel_modes)])}',
+            f'  block_length:          {",".join([str(x) for x in flags_to_list(self.block_length, SBC_BLOCK_LENGTHS)])}',
+            f'  subbands:              {",".join([str(x) for x in flags_to_list(self.subbands, SBC_SUBBANDS)])}',
+            f'  allocation_method:     {",".join([str(x) for x in flags_to_list(self.allocation_method, allocation_methods)])}',
+            f'  minimum_bitpool_value: {self.minimum_bitpool_value}',
+            f'  maximum_bitpool_value: {self.maximum_bitpool_value}'
+            ')'
+        ])
+
+
+# -----------------------------------------------------------------------------
+class AacMediaCodecInformation(
+    namedtuple(
+        'AacMediaCodecInformation',
+        [
+            'object_type',
+            'sampling_frequency',
+            'channels',
+            'vbr',
+            'bitrate'
+        ]
+    )
+):
+    '''
+    A2DP spec - 4.5.2 Codec Specific Information Elements
+    '''
+
+    BIT_FIELDS = 'u8u12u2p2u1u23'
+    OBJECT_TYPE_BITS = {
+        MPEG_2_AAC_LC_OBJECT_TYPE:       1 << 7,
+        MPEG_4_AAC_LC_OBJECT_TYPE:       1 << 6,
+        MPEG_4_AAC_LTP_OBJECT_TYPE:      1 << 5,
+        MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 1 << 4
+    }
+    SAMPLING_FREQUENCY_BITS = {
+        8000:  1 << 11,
+        11025: 1 << 10,
+        12000: 1 << 9,
+        16000: 1 << 8,
+        22050: 1 << 7,
+        24000: 1 << 6,
+        32000: 1 << 5,
+        44100: 1 << 4,
+        48000: 1 << 3,
+        64000: 1 << 2,
+        88200: 1 << 1,
+        96000: 1
+    }
+    CHANNELS_BITS = {
+        1: 1 << 1,
+        2: 1
+    }
+
+    @staticmethod
+    def from_bytes(data):
+        return AacMediaCodecInformation(*bitstruct.unpack(AacMediaCodecInformation.BIT_FIELDS, data))
+
+    @classmethod
+    def from_discrete_values(
+        cls,
+        object_type,
+        sampling_frequency,
+        channels,
+        vbr,
+        bitrate
+    ):
+        return AacMediaCodecInformation(
+            object_type           = cls.OBJECT_TYPE_BITS[object_type],
+            sampling_frequency    = cls.SAMPLING_FREQUENCY_BITS[sampling_frequency],
+            channels              = cls.CHANNELS_BITS[channels],
+            vbr                   = vbr,
+            bitrate               = bitrate
+        )
+
+    @classmethod
+    def from_lists(
+        cls,
+        object_types,
+        sampling_frequencies,
+        channels,
+        vbr,
+        bitrate
+    ):
+        return AacMediaCodecInformation(
+            object_type           = sum(cls.OBJECT_TYPE_BITS[x] for x in object_types),
+            sampling_frequency    = sum(cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies),
+            channels              = sum(cls.CHANNELS_BITS[x] for x in channels),
+            vbr                   = vbr,
+            bitrate               = bitrate
+        )
+
+    def __bytes__(self):
+        return bitstruct.pack(self.BIT_FIELDS, *self)
+
+    def __str__(self):
+        object_types = ['MPEG_2_AAC_LC', 'MPEG_4_AAC_LC', 'MPEG_4_AAC_LTP', 'MPEG_4_AAC_SCALABLE', '[4]', '[5]', '[6]', '[7]']
+        channels = [1, 2]
+        return '\n'.join([
+            'AacMediaCodecInformation(',
+            f'  object_type:        {",".join([str(x) for x in flags_to_list(self.object_type, object_types)])}',
+            f'  sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, MPEG_2_4_AAC_SAMPLING_FREQUENCIES)])}',
+            f'  channels:           {",".join([str(x) for x in flags_to_list(self.channels, channels)])}',
+            f'  vbr:                {self.vbr}',
+            f'  bitrate:            {self.bitrate}'
+            ')'
+        ])
+
+
+# -----------------------------------------------------------------------------
+class VendorSpecificMediaCodecInformation:
+    '''
+    A2DP spec - 4.7.2 Codec Specific Information Elements
+    '''
+
+    @staticmethod
+    def from_bytes(data):
+        (vendor_id, codec_id) = struct.unpack_from('<IH', data, 0)
+        return VendorSpecificMediaCodecInformation(vendor_id, codec_id, data[6:])
+
+    def __init__(self, vendor_id, codec_id, value):
+        self.vendor_id = vendor_id
+        self.codec_id  = codec_id
+        self.value     = value
+
+    def __bytes__(self):
+        return struct.pack('<IH', self.vendor_id, self.codec_id, self.value)
+
+    def __str__(self):
+        return '\n'.join([
+            'VendorSpecificMediaCodecInformation(',
+            f'  vendor_id: {self.vendor_id:08X} ({name_or_number(COMPANY_IDENTIFIERS, self.vendor_id & 0xFFFF)})',
+            f'  codec_id:  {self.codec_id:04X}',
+            f'  value:     {self.value.hex()}'
+            ')'
+        ])
+
+
+# -----------------------------------------------------------------------------
+class SbcFrame:
+    def __init__(
+        self,
+        sampling_frequency,
+        block_count,
+        channel_mode,
+        subband_count,
+        payload
+    ):
+        self.sampling_frequency = sampling_frequency
+        self.block_count        = block_count
+        self.channel_mode       = channel_mode
+        self.subband_count      = subband_count
+        self.payload            = payload
+
+    @property
+    def sample_count(self):
+        return self.subband_count * self.block_count
+
+    @property
+    def bitrate(self):
+        return 8 * ((len(self.payload) * self.sampling_frequency) // self.sample_count)
+
+    @property
+    def duration(self):
+        return self.sample_count / self.sampling_frequency
+
+    def __str__(self):
+        return f'SBC(sf={self.sampling_frequency},cm={self.channel_mode},br={self.bitrate},sc={self.sample_count},size={len(self.payload)})'
+
+
+# -----------------------------------------------------------------------------
+class SbcParser:
+    def __init__(self, read):
+        self.read = read
+
+    @property
+    def frames(self):
+        async def generate_frames():
+            while True:
+                # Read 4 bytes of header
+                header = await self.read(4)
+                if len(header) != 4:
+                    return
+
+                # Check the sync word
+                if header[0] != SBC_SYNC_WORD:
+                    logger.debug('invalid sync word')
+                    return
+
+                # Extract some of the header fields
+                sampling_frequency = SBC_SAMPLING_FREQUENCIES[(header[1] >> 6) & 3]
+                blocks             = 4 * (1 + ((header[1] >> 4) & 3))
+                channel_mode       = (header[1] >> 2) & 3
+                channels           = 1 if channel_mode == SBC_MONO_CHANNEL_MODE else 2
+                subbands           = 8 if ((header[1]) & 1) else 4
+                bitpool            = header[2]
+
+                # Compute the frame length
+                frame_length = 4 + (4 * subbands * channels) // 8
+                if channel_mode in (SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE):
+                    frame_length += (blocks * channels * bitpool) // 8
+                else:
+                    frame_length += ((1 if channel_mode == SBC_JOINT_STEREO_CHANNEL_MODE else 0) * subbands + blocks * bitpool) // 8
+
+                # Read the rest of the frame
+                payload = header + await self.read(frame_length - 4)
+
+                # Emit the next frame
+                yield SbcFrame(sampling_frequency, blocks, channel_mode, subbands, payload)
+
+        return generate_frames()
+
+
+# -----------------------------------------------------------------------------
+class SbcPacketSource:
+    def __init__(self, read, mtu, codec_capabilities):
+        self.read               = read
+        self.mtu                = mtu
+        self.codec_capabilities = codec_capabilities
+
+    @property
+    def packets(self):
+        async def generate_packets():
+            from .avdtp import MediaPacket  # Import here to avoid a circular reference
+
+            sequence_number = 0
+            timestamp       = 0
+            frames          = []
+            frames_size     = 0
+            max_rtp_payload = self.mtu - 12 - 1
+
+            # NOTE: this doesn't support frame fragments
+            sbc_parser = SbcParser(self.read)
+            async for frame in sbc_parser.frames:
+                print(frame)
+
+                if frames_size + len(frame.payload) > max_rtp_payload or len(frames) == 16:
+                    # Need to flush what has been accumulated so far
+
+                    # Emit a packet
+                    sbc_payload = bytes([len(frames)]) + b''.join([frame.payload for frame in frames])
+                    packet = MediaPacket(2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, sbc_payload)
+                    packet.timestamp_seconds = timestamp / frame.sampling_frequency
+                    yield packet
+
+                    # Prepare for next packets
+                    sequence_number += 1
+                    timestamp += sum([frame.sample_count for frame in frames])
+                    frames = [frame]
+                    frames_size = len(frame.payload)
+                else:
+                    # Accumulate
+                    frames.append(frame)
+                    frames_size += len(frame.payload)
+
+        return generate_packets()
diff --git a/bumble/att.py b/bumble/att.py
new file mode 100644
index 0000000..22b683e
--- /dev/null
+++ b/bumble/att.py
@@ -0,0 +1,728 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# ATT - Attribute Protocol
+#
+# See Bluetooth spec @ Vol 3, Part F
+#
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from colors import color
+from pyee import EventEmitter
+
+from .core import *
+from .hci import *
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+ATT_CID = 0x04
+
+ATT_ERROR_RESPONSE              = 0x01
+ATT_EXCHANGE_MTU_REQUEST        = 0x02
+ATT_EXCHANGE_MTU_RESPONSE       = 0x03
+ATT_FIND_INFORMATION_REQUEST    = 0x04
+ATT_FIND_INFORMATION_RESPONSE   = 0x05
+ATT_FIND_BY_TYPE_VALUE_REQUEST  = 0x06
+ATT_FIND_BY_TYPE_VALUE_RESPONSE = 0x07
+ATT_READ_BY_TYPE_REQUEST        = 0x08
+ATT_READ_BY_TYPE_RESPONSE       = 0x09
+ATT_READ_REQUEST                = 0x0A
+ATT_READ_RESPONSE               = 0x0B
+ATT_READ_BLOB_REQUEST           = 0x0C
+ATT_READ_BLOB_RESPONSE          = 0x0D
+ATT_READ_MULTIPLE_REQUEST       = 0x0E
+ATT_READ_MULTIPLE_RESPONSE      = 0x0F
+ATT_READ_BY_GROUP_TYPE_REQUEST  = 0x10
+ATT_READ_BY_GROUP_TYPE_RESPONSE = 0x11
+ATT_WRITE_REQUEST               = 0x12
+ATT_WRITE_RESPONSE              = 0x13
+ATT_WRITE_COMMAND               = 0x52
+ATT_SIGNED_WRITE_COMMAND        = 0xD2
+ATT_PREPARE_WRITE_REQUEST       = 0x16
+ATT_PREPARE_WRITE_RESPONSE      = 0x17
+ATT_EXECUTE_WRITE_REQUEST       = 0x18
+ATT_EXECUTE_WRITE_RESPONSE      = 0x19
+ATT_HANDLE_VALUE_NOTIFICATION   = 0x1B
+ATT_HANDLE_VALUE_INDICATION     = 0x1D
+ATT_HANDLE_VALUE_CONFIRMATION   = 0x1E
+
+ATT_PDU_NAMES = {
+    ATT_ERROR_RESPONSE:              'ATT_ERROR_RESPONSE',
+    ATT_EXCHANGE_MTU_REQUEST:        'ATT_EXCHANGE_MTU_REQUEST',
+    ATT_EXCHANGE_MTU_RESPONSE:       'ATT_EXCHANGE_MTU_RESPONSE',
+    ATT_FIND_INFORMATION_REQUEST:    'ATT_FIND_INFORMATION_REQUEST',
+    ATT_FIND_INFORMATION_RESPONSE:   'ATT_FIND_INFORMATION_RESPONSE',
+    ATT_FIND_BY_TYPE_VALUE_REQUEST:  'ATT_FIND_BY_TYPE_VALUE_REQUEST',
+    ATT_FIND_BY_TYPE_VALUE_RESPONSE: 'ATT_FIND_BY_TYPE_VALUE_RESPONSE',
+    ATT_READ_BY_TYPE_REQUEST:        'ATT_READ_BY_TYPE_REQUEST',
+    ATT_READ_BY_TYPE_RESPONSE:       'ATT_READ_BY_TYPE_RESPONSE',
+    ATT_READ_REQUEST:                'ATT_READ_REQUEST',
+    ATT_READ_RESPONSE:               'ATT_READ_RESPONSE',
+    ATT_READ_BLOB_REQUEST:           'ATT_READ_BLOB_REQUEST',
+    ATT_READ_BLOB_RESPONSE:          'ATT_READ_BLOB_RESPONSE',
+    ATT_READ_MULTIPLE_REQUEST:       'ATT_READ_MULTIPLE_REQUEST',
+    ATT_READ_MULTIPLE_RESPONSE:      'ATT_READ_MULTIPLE_RESPONSE',
+    ATT_READ_BY_GROUP_TYPE_REQUEST:  'ATT_READ_BY_GROUP_TYPE_REQUEST',
+    ATT_READ_BY_GROUP_TYPE_RESPONSE: 'ATT_READ_BY_GROUP_TYPE_RESPONSE',
+    ATT_WRITE_REQUEST:               'ATT_WRITE_REQUEST',
+    ATT_WRITE_RESPONSE:              'ATT_WRITE_RESPONSE',
+    ATT_WRITE_COMMAND:               'ATT_WRITE_COMMAND',
+    ATT_SIGNED_WRITE_COMMAND:        'ATT_SIGNED_WRITE_COMMAND',
+    ATT_PREPARE_WRITE_REQUEST:       'ATT_PREPARE_WRITE_REQUEST',
+    ATT_PREPARE_WRITE_RESPONSE:      'ATT_PREPARE_WRITE_RESPONSE',
+    ATT_EXECUTE_WRITE_REQUEST:       'ATT_EXECUTE_WRITE_REQUEST',
+    ATT_EXECUTE_WRITE_RESPONSE:      'ATT_EXECUTE_WRITE_RESPONSE',
+    ATT_HANDLE_VALUE_NOTIFICATION:   'ATT_HANDLE_VALUE_NOTIFICATION',
+    ATT_HANDLE_VALUE_INDICATION:     'ATT_HANDLE_VALUE_INDICATION',
+    ATT_HANDLE_VALUE_CONFIRMATION:   'ATT_HANDLE_VALUE_CONFIRMATION'
+}
+
+ATT_REQUESTS = [
+    ATT_EXCHANGE_MTU_REQUEST,
+    ATT_FIND_INFORMATION_REQUEST,
+    ATT_FIND_BY_TYPE_VALUE_REQUEST,
+    ATT_READ_BY_TYPE_REQUEST,
+    ATT_READ_REQUEST,
+    ATT_READ_BLOB_REQUEST,
+    ATT_READ_MULTIPLE_REQUEST,
+    ATT_READ_BY_GROUP_TYPE_REQUEST,
+    ATT_WRITE_REQUEST,
+    ATT_PREPARE_WRITE_REQUEST,
+    ATT_EXECUTE_WRITE_REQUEST
+]
+
+ATT_RESPONSES = [
+    ATT_ERROR_RESPONSE,
+    ATT_EXCHANGE_MTU_RESPONSE,
+    ATT_FIND_INFORMATION_RESPONSE,
+    ATT_FIND_BY_TYPE_VALUE_RESPONSE,
+    ATT_READ_BY_TYPE_RESPONSE,
+    ATT_READ_RESPONSE,
+    ATT_READ_BLOB_RESPONSE,
+    ATT_READ_MULTIPLE_RESPONSE,
+    ATT_READ_BY_GROUP_TYPE_RESPONSE,
+    ATT_WRITE_RESPONSE,
+    ATT_PREPARE_WRITE_RESPONSE,
+    ATT_EXECUTE_WRITE_RESPONSE
+]
+
+ATT_INVALID_HANDLE_ERROR                   = 0x01
+ATT_READ_NOT_PERMITTED_ERROR               = 0x02
+ATT_WRITE_NOT_PERMITTED_ERROR              = 0x03
+ATT_INVALID_PDU_ERROR                      = 0x04
+ATT_INSUFFICIENT_AUTHENTICATION_ERROR      = 0x05
+ATT_REQUEST_NOT_SUPPORTED_ERROR            = 0x06
+ATT_INVALID_OFFSET_ERROR                   = 0x07
+ATT_INSUFFICIENT_AUTHORIZATION_ERROR       = 0x08
+ATT_PREPARE_QUEUE_FULL_ERROR               = 0x09
+ATT_ATTRIBUTE_NOT_FOUND_ERROR              = 0x0A
+ATT_ATTRIBUTE_NOT_LONG_ERROR               = 0x0B
+ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE_ERROR = 0x0C
+ATT_INVALID_ATTRIBUTE_LENGTH_ERROR         = 0x0D
+ATT_UNLIKELY_ERROR_ERROR                   = 0x0E
+ATT_INSUFFICIENT_ENCRYPTION_ERROR          = 0x0F
+ATT_UNSUPPORTED_GROUP_TYPE_ERROR           = 0x10
+ATT_INSUFFICIENT_RESOURCES_ERROR           = 0x11
+
+ATT_ERROR_NAMES = {
+    ATT_INVALID_HANDLE_ERROR:                   'ATT_INVALID_HANDLE_ERROR',
+    ATT_READ_NOT_PERMITTED_ERROR:               'ATT_READ_NOT_PERMITTED_ERROR',
+    ATT_WRITE_NOT_PERMITTED_ERROR:              'ATT_WRITE_NOT_PERMITTED_ERROR',
+    ATT_INVALID_PDU_ERROR:                      'ATT_INVALID_PDU_ERROR',
+    ATT_INSUFFICIENT_AUTHENTICATION_ERROR:      'ATT_INSUFFICIENT_AUTHENTICATION_ERROR',
+    ATT_REQUEST_NOT_SUPPORTED_ERROR:            'ATT_REQUEST_NOT_SUPPORTED_ERROR',
+    ATT_INVALID_OFFSET_ERROR:                   'ATT_INVALID_OFFSET_ERROR',
+    ATT_INSUFFICIENT_AUTHORIZATION_ERROR:       'ATT_INSUFFICIENT_AUTHORIZATION_ERROR',
+    ATT_PREPARE_QUEUE_FULL_ERROR:               'ATT_PREPARE_QUEUE_FULL_ERROR',
+    ATT_ATTRIBUTE_NOT_FOUND_ERROR:              'ATT_ATTRIBUTE_NOT_FOUND_ERROR',
+    ATT_ATTRIBUTE_NOT_LONG_ERROR:               'ATT_ATTRIBUTE_NOT_LONG_ERROR',
+    ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE_ERROR: 'ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE_ERROR',
+    ATT_INVALID_ATTRIBUTE_LENGTH_ERROR:         'ATT_INVALID_ATTRIBUTE_LENGTH_ERROR',
+    ATT_UNLIKELY_ERROR_ERROR:                   'ATT_UNLIKELY_ERROR_ERROR',
+    ATT_INSUFFICIENT_ENCRYPTION_ERROR:          'ATT_INSUFFICIENT_ENCRYPTION_ERROR',
+    ATT_UNSUPPORTED_GROUP_TYPE_ERROR:           'ATT_UNSUPPORTED_GROUP_TYPE_ERROR',
+    ATT_INSUFFICIENT_RESOURCES_ERROR:           'ATT_INSUFFICIENT_RESOURCES_ERROR'
+}
+
+ATT_DEFAULT_MTU = 23
+
+HANDLE_FIELD_SPEC    = {'size': 2, 'mapper': lambda x: f'0x{x:04X}'}
+UUID_2_16_FIELD_SPEC = lambda x, y: UUID.parse_uuid(x, y)    # noqa: E731
+UUID_2_FIELD_SPEC    = lambda x, y: UUID.parse_uuid_2(x, y)  # noqa: E731
+
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+def key_with_value(dictionary, target_value):
+    for key, value in dictionary.items():
+        if value == target_value:
+            return key
+    return None
+
+
+# -----------------------------------------------------------------------------
+# Exceptions
+# -----------------------------------------------------------------------------
+class ATT_Error(Exception):
+    def __init__(self, error_code, att_handle=0x0000):
+        self.error_code = error_code
+        self.att_handle = att_handle
+
+    def __str__(self):
+        return f'ATT_Error({ATT_PDU.error_name(self.error_code)})'
+
+
+# -----------------------------------------------------------------------------
+# Attribute Protocol
+# -----------------------------------------------------------------------------
+class ATT_PDU:
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.3 ATTRIBUTE PDU
+    '''
+    pdu_classes = {}
+    op_code = 0
+
+    @staticmethod
+    def from_bytes(pdu):
+        op_code = pdu[0]
+
+        cls = ATT_PDU.pdu_classes.get(op_code)
+        if cls is None:
+            instance = ATT_PDU(pdu)
+            instance.name = ATT_PDU.pdu_name(op_code)
+            instance.op_code = op_code
+            return instance
+        self = cls.__new__(cls)
+        ATT_PDU.__init__(self, pdu)
+        if hasattr(self, 'fields'):
+            self.init_from_bytes(pdu, 1)
+        return self
+
+    @staticmethod
+    def pdu_name(op_code):
+        return name_or_number(ATT_PDU_NAMES, op_code, 2)
+
+    @staticmethod
+    def error_name(error_code):
+        return name_or_number(ATT_ERROR_NAMES, error_code, 2)
+
+    @staticmethod
+    def subclass(fields):
+        def inner(cls):
+            cls.name = cls.__name__.upper()
+            cls.op_code = key_with_value(ATT_PDU_NAMES, cls.name)
+            if cls.op_code is None:
+                raise KeyError(f'PDU name {cls.name} not found in ATT_PDU_NAMES')
+            cls.fields = fields
+
+            # Register a factory for this class
+            ATT_PDU.pdu_classes[cls.op_code] = cls
+
+            return cls
+
+        return inner
+
+    def __init__(self, pdu=None, **kwargs):
+        if hasattr(self, 'fields') and kwargs:
+            HCI_Object.init_from_fields(self, self.fields, kwargs)
+        if pdu is None:
+            pdu = bytes([self.op_code]) + HCI_Object.dict_to_bytes(kwargs, self.fields)
+        self.pdu = pdu
+
+    def init_from_bytes(self, pdu, offset):
+        return HCI_Object.init_from_bytes(self, pdu, offset, self.fields)
+
+    def to_bytes(self):
+        return self.pdu
+
+    @property
+    def is_command(self):
+        return ((self.op_code >> 6) & 1) == 1
+
+    @property
+    def has_authentication_signature(self):
+        return ((self.op_code >> 7) & 1) == 1
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        result = color(self.name, 'yellow')
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        else:
+            if len(self.pdu) > 1:
+                result += f': {self.pdu.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('request_opcode_in_error',   {'size': 1, 'mapper': ATT_PDU.pdu_name}),
+    ('attribute_handle_in_error', HANDLE_FIELD_SPEC),
+    ('error_code',                {'size': 1, 'mapper': ATT_PDU.error_name})
+])
+class ATT_Error_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.1.1 Error Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('client_rx_mtu', 2)
+])
+class ATT_Exchange_MTU_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.2.1 Exchange MTU Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('server_rx_mtu', 2)
+])
+class ATT_Exchange_MTU_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.2.2 Exchange MTU Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('starting_handle', HANDLE_FIELD_SPEC),
+    ('ending_handle',   HANDLE_FIELD_SPEC)
+])
+class ATT_Find_Information_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.3.1 Find Information Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('format',           1),
+    ('information_data', '*')
+])
+class ATT_Find_Information_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.3.2 Find Information Response
+    '''
+
+    def parse_information_data(self):
+        self.information = []
+        offset = 0
+        uuid_size = 2 if self.format == 1 else 16
+        while offset + uuid_size <= len(self.information_data):
+            handle = struct.unpack_from('<H', self.information_data, offset)[0]
+            uuid   = self.information_data[2 + offset:2 + offset + uuid_size]
+            self.information.append((handle, uuid))
+            offset += 2 + uuid_size
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.parse_information_data()
+
+    def init_from_bytes(self, pdu, offset):
+        super().init_from_bytes(pdu, offset)
+        self.parse_information_data()
+
+    def __str__(self):
+        result = color(self.name, 'yellow')
+        result += ':\n' + HCI_Object.format_fields(self.__dict__, [
+            ('format',       1),
+            ('information', {'mapper': lambda x: ', '.join([f'0x{handle:04X}:{uuid.hex()}' for handle, uuid in x])})
+        ], '  ')
+        return result
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('starting_handle', HANDLE_FIELD_SPEC),
+    ('ending_handle',   HANDLE_FIELD_SPEC),
+    ('attribute_type',  UUID_2_FIELD_SPEC),
+    ('attribute_value', '*')
+])
+class ATT_Find_By_Type_Value_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.3.3 Find By Type Value Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('handles_information_list', '*')
+])
+class ATT_Find_By_Type_Value_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.3.4 Find By Type Value Response
+    '''
+
+    def parse_handles_information_list(self):
+        self.handles_information = []
+        offset = 0
+        while offset + 4 <= len(self.handles_information_list):
+            found_attribute_handle, group_end_handle = struct.unpack_from('<HH', self.handles_information_list, offset)
+            self.handles_information.append((found_attribute_handle, group_end_handle))
+            offset += 4
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.parse_handles_information_list()
+
+    def init_from_bytes(self, pdu, offset):
+        super().init_from_bytes(pdu, offset)
+        self.parse_handles_information_list()
+
+    def __str__(self):
+        result = color(self.name, 'yellow')
+        result += ':\n' + HCI_Object.format_fields(self.__dict__, [
+            ('handles_information', {'mapper': lambda x: ', '.join([f'0x{handle1:04X}-0x{handle2:04X}' for handle1, handle2 in x])})
+        ], '  ')
+        return result
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('starting_handle', HANDLE_FIELD_SPEC),
+    ('ending_handle',   HANDLE_FIELD_SPEC),
+    ('attribute_type',  UUID_2_16_FIELD_SPEC)
+])
+class ATT_Read_By_Type_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.1 Read By Type Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('length',              1),
+    ('attribute_data_list', '*')
+])
+class ATT_Read_By_Type_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.2 Read By Type Response
+    '''
+
+    def parse_attribute_data_list(self):
+        self.attributes = []
+        offset = 0
+        while self.length != 0 and offset + self.length <= len(self.attribute_data_list):
+            attribute_handle, = struct.unpack_from('<H', self.attribute_data_list, offset)
+            attribute_value = self.attribute_data_list[offset + 2:offset + self.length]
+            self.attributes.append((attribute_handle, attribute_value))
+            offset += self.length
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.parse_attribute_data_list()
+
+    def init_from_bytes(self, pdu, offset):
+        super().init_from_bytes(pdu, offset)
+        self.parse_attribute_data_list()
+
+    def __str__(self):
+        result = color(self.name, 'yellow')
+        result += ':\n' + HCI_Object.format_fields(self.__dict__, [
+            ('length',     1),
+            ('attributes', {'mapper': lambda x: ', '.join([f'0x{handle:04X}:{value.hex()}' for handle, value in x])})
+        ], '  ')
+        return result
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC)
+])
+class ATT_Read_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.3 Read Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_value', '*')
+])
+class ATT_Read_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.4 Read Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC),
+    ('value_offset',     2)
+])
+class ATT_Read_Blob_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.5 Read Blob Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('part_attribute_value', '*')
+])
+class ATT_Read_Blob_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.6 Read Blob Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('set_of_handles', '*')
+])
+class ATT_Read_Multiple_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.7 Read Multiple Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('set_of_values', '*')
+])
+class ATT_Read_Multiple_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.8 Read Multiple Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('starting_handle',      HANDLE_FIELD_SPEC),
+    ('ending_handle',        HANDLE_FIELD_SPEC),
+    ('attribute_group_type', UUID_2_16_FIELD_SPEC)
+])
+class ATT_Read_By_Group_Type_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.9 Read by Group Type Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('length',              1),
+    ('attribute_data_list', '*')
+])
+class ATT_Read_By_Group_Type_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.4.10 Read by Group Type Response
+    '''
+
+    def parse_attribute_data_list(self):
+        self.attributes = []
+        offset = 0
+        while self.length != 0 and offset + self.length <= len(self.attribute_data_list):
+            attribute_handle, end_group_handle = struct.unpack_from('<HH', self.attribute_data_list, offset)
+            attribute_value = self.attribute_data_list[offset + 4:offset + self.length]
+            self.attributes.append((attribute_handle, end_group_handle, attribute_value))
+            offset += self.length
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.parse_attribute_data_list()
+
+    def init_from_bytes(self, pdu, offset):
+        super().init_from_bytes(pdu, offset)
+        self.parse_attribute_data_list()
+
+    def __str__(self):
+        result = color(self.name, 'yellow')
+        result += ':\n' + HCI_Object.format_fields(self.__dict__, [
+            ('length',     1),
+            ('attributes', {'mapper': lambda x: ', '.join([f'0x{handle:04X}-0x{end:04X}:{value.hex()}' for handle, end, value in x])})
+        ], '  ')
+        return result
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC),
+    ('attribute_value',  '*')
+])
+class ATT_Write_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.5.1 Write Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([])
+class ATT_Write_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.5.2 Write Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC),
+    ('attribute_value',  '*')
+])
+class ATT_Write_Command(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.5.3 Write Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC),
+    ('attribute_value',  '*')
+    # ('authentication_signature', 'TODO')
+])
+class ATT_Signed_Write_Command(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.5.4 Signed Write Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle',     HANDLE_FIELD_SPEC),
+    ('value_offset',         2),
+    ('part_attribute_value', '*')
+])
+class ATT_Prepare_Write_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.6.1 Prepare Write Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle',     HANDLE_FIELD_SPEC),
+    ('value_offset',         2),
+    ('part_attribute_value', '*')
+])
+class ATT_Prepare_Write_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.6.2 Prepare Write Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([])
+class ATT_Execute_Write_Request(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.6.3 Execute Write Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([])
+class ATT_Execute_Write_Response(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.6.4 Execute Write Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC),
+    ('attribute_value',  '*')
+])
+class ATT_Handle_Value_Notification(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.7.1 Handle Value Notification
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([
+    ('attribute_handle', HANDLE_FIELD_SPEC),
+    ('attribute_value',  '*')
+])
+class ATT_Handle_Value_Indication(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.7.2 Handle Value Indication
+    '''
+
+
+# -----------------------------------------------------------------------------
+@ATT_PDU.subclass([])
+class ATT_Handle_Value_Confirmation(ATT_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part F - 3.4.7.3 Handle Value Confirmation
+    '''
+
+
+# -----------------------------------------------------------------------------
+class Attribute(EventEmitter):
+    # Permission flags
+    READABLE                      = 0x01
+    WRITEABLE                     = 0x02
+    READ_REQUIRES_ENCRYPTION      = 0x04
+    WRITE_REQUIRES_ENCRYPTION     = 0x08
+    READ_REQUIRES_AUTHENTICATION  = 0x10
+    WRITE_REQUIRES_AUTHENTICATION = 0x20
+    READ_REQUIRES_AUTHORIZATION   = 0x40
+    WRITE_REQUIRES_AUTHORIZATION  = 0x80
+
+    def __init__(self, attribute_type, permissions, value = b''):
+        EventEmitter.__init__(self)
+        self.handle           = 0
+        self.end_group_handle = 0
+        self.permissions      = permissions
+
+        # Convert the type to a UUID object if it isn't already
+        if type(attribute_type) is str:
+            self.type = UUID(attribute_type)
+        elif type(attribute_type) is bytes:
+            self.type = UUID.from_bytes(attribute_type)
+        else:
+            self.type = attribute_type
+
+        # Convert the value to a byte array
+        if type(value) is str:
+            self.value = bytes(value, 'utf-8')
+        else:
+            self.value = value
+
+    def read_value(self, connection):
+        if read := getattr(self.value, 'read', None):
+            try:
+                return read(connection)
+            except ATT_Error as error:
+                raise ATT_Error(error_code=error.error_code, att_handle=self.handle)
+        else:
+            return self.value
+
+    def write_value(self, connection, value):
+        if write := getattr(self.value, 'write', None):
+            try:
+                write(connection, value)
+            except ATT_Error as error:
+                raise ATT_Error(error_code=error.error_code, att_handle=self.handle)
+        else:
+            self.value = value
+
+        self.emit('write', connection, value)
+
+    def __repr__(self):
+        if len(self.value) > 0:
+            value_string = f', value={self.value.hex()}'
+        else:
+            value_string = ''
+        return f'Attribute(handle=0x{self.handle:04X}, type={self.type}, permissions={self.permissions}{value_string})'
diff --git a/bumble/avdtp.py b/bumble/avdtp.py
new file mode 100644
index 0000000..759e38c
--- /dev/null
+++ b/bumble/avdtp.py
@@ -0,0 +1,1921 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import struct
+import time
+import logging
+from colors import color
+from pyee import EventEmitter
+
+from .core import (
+    BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
+    InvalidStateError,
+    ProtocolError,
+    name_or_number
+)
+from .a2dp import (
+    A2DP_CODEC_TYPE_NAMES,
+    A2DP_MPEG_2_4_AAC_CODEC_TYPE,
+    A2DP_NON_A2DP_CODEC_TYPE,
+    A2DP_SBC_CODEC_TYPE,
+    AacMediaCodecInformation,
+    SbcMediaCodecInformation,
+    VendorSpecificMediaCodecInformation
+)
+from . import sdp
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+AVDTP_PSM = 0x0019
+
+AVDTP_DEFAULT_RTX_SIG_TIMER = 5  # Seconds
+
+# Signal Identifiers (AVDTP spec - 8.5 Signal Command Set)
+AVDTP_DISCOVER             = 0x01
+AVDTP_GET_CAPABILITIES     = 0x02
+AVDTP_SET_CONFIGURATION    = 0x03
+AVDTP_GET_CONFIGURATION    = 0x04
+AVDTP_RECONFIGURE          = 0x05
+AVDTP_OPEN                 = 0x06
+AVDTP_START                = 0x07
+AVDTP_CLOSE                = 0x08
+AVDTP_SUSPEND              = 0x09
+AVDTP_ABORT                = 0x0A
+AVDTP_SECURITY_CONTROL     = 0x0B
+AVDTP_GET_ALL_CAPABILITIES = 0x0C
+AVDTP_DELAYREPORT          = 0x0D
+
+AVDTP_SIGNAL_NAMES = {
+    AVDTP_DISCOVER:             'AVDTP_DISCOVER',
+    AVDTP_GET_CAPABILITIES:     'AVDTP_GET_CAPABILITIES',
+    AVDTP_SET_CONFIGURATION:    'AVDTP_SET_CONFIGURATION',
+    AVDTP_GET_CONFIGURATION:    'AVDTP_GET_CONFIGURATION',
+    AVDTP_RECONFIGURE:          'AVDTP_RECONFIGURE',
+    AVDTP_OPEN:                 'AVDTP_OPEN',
+    AVDTP_START:                'AVDTP_START',
+    AVDTP_CLOSE:                'AVDTP_CLOSE',
+    AVDTP_SUSPEND:              'AVDTP_SUSPEND',
+    AVDTP_ABORT:                'AVDTP_ABORT',
+    AVDTP_SECURITY_CONTROL:     'AVDTP_SECURITY_CONTROL',
+    AVDTP_GET_ALL_CAPABILITIES: 'AVDTP_GET_ALL_CAPABILITIES',
+    AVDTP_DELAYREPORT:          'AVDTP_DELAYREPORT'
+}
+
+AVDTP_SIGNAL_IDENTIFIERS = {
+    'AVDTP_DISCOVER':             AVDTP_DISCOVER,
+    'AVDTP_GET_CAPABILITIES':     AVDTP_GET_CAPABILITIES,
+    'AVDTP_SET_CONFIGURATION':    AVDTP_SET_CONFIGURATION,
+    'AVDTP_GET_CONFIGURATION':    AVDTP_GET_CONFIGURATION,
+    'AVDTP_RECONFIGURE':          AVDTP_RECONFIGURE,
+    'AVDTP_OPEN':                 AVDTP_OPEN,
+    'AVDTP_START':                AVDTP_START,
+    'AVDTP_CLOSE':                AVDTP_CLOSE,
+    'AVDTP_SUSPEND':              AVDTP_SUSPEND,
+    'AVDTP_ABORT':                AVDTP_ABORT,
+    'AVDTP_SECURITY_CONTROL':     AVDTP_SECURITY_CONTROL,
+    'AVDTP_GET_ALL_CAPABILITIES': AVDTP_GET_ALL_CAPABILITIES,
+    'AVDTP_DELAYREPORT':          AVDTP_DELAYREPORT
+}
+
+# Error codes (AVDTP spec - 8.20.6.2 ERROR_CODE tables)
+AVDTP_BAD_HEADER_FORMAT_ERROR          = 0x01
+AVDTP_BAD_LENGTH_ERROR                 = 0x11
+AVDTP_BAD_ACP_SEID_ERROR               = 0x12
+AVDTP_SEP_IN_USE_ERROR                 = 0x13
+AVDTP_SEP_NOT_IN_USE_ERROR             = 0x14
+AVDTP_BAD_SERV_CATEGORY_ERROR          = 0x17
+AVDTP_BAD_PAYLOAD_FORMAT_ERROR         = 0x18
+AVDTP_NOT_SUPPORTED_COMMAND_ERROR      = 0x19
+AVDTP_INVALID_CAPABILITIES_ERROR       = 0x1A
+AVDTP_BAD_RECOVERY_TYPE_ERROR          = 0x22
+AVDTP_BAD_MEDIA_TRANSPORT_FORMAT_ERROR = 0x23
+AVDTP_BAD_RECOVERY_FORMAT_ERROR        = 0x25
+AVDTP_BAD_ROHC_FORMAT_ERROR            = 0x26
+AVDTP_BAD_CP_FORMAT_ERROR              = 0x27
+AVDTP_BAD_MULTIPLEXING_FORMAT_ERROR    = 0x28
+AVDTP_UNSUPPORTED_CONFIGURATION_ERROR  = 0x29
+AVDTP_BAD_STATE_ERROR                  = 0x31
+
+AVDTP_ERROR_NAMES = {
+    AVDTP_BAD_HEADER_FORMAT_ERROR:          'AVDTP_BAD_HEADER_FORMAT_ERROR',
+    AVDTP_BAD_LENGTH_ERROR:                 'AVDTP_BAD_LENGTH_ERROR',
+    AVDTP_BAD_ACP_SEID_ERROR:               'AVDTP_BAD_ACP_SEID_ERROR',
+    AVDTP_SEP_IN_USE_ERROR:                 'AVDTP_SEP_IN_USE_ERROR',
+    AVDTP_SEP_NOT_IN_USE_ERROR:             'AVDTP_SEP_NOT_IN_USE_ERROR',
+    AVDTP_BAD_SERV_CATEGORY_ERROR:          'AVDTP_BAD_SERV_CATEGORY_ERROR',
+    AVDTP_BAD_PAYLOAD_FORMAT_ERROR:         'AVDTP_BAD_PAYLOAD_FORMAT_ERROR',
+    AVDTP_NOT_SUPPORTED_COMMAND_ERROR:      'AVDTP_NOT_SUPPORTED_COMMAND_ERROR',
+    AVDTP_INVALID_CAPABILITIES_ERROR:       'AVDTP_INVALID_CAPABILITIES_ERROR',
+    AVDTP_BAD_RECOVERY_TYPE_ERROR:          'AVDTP_BAD_RECOVERY_TYPE_ERROR',
+    AVDTP_BAD_MEDIA_TRANSPORT_FORMAT_ERROR: 'AVDTP_BAD_MEDIA_TRANSPORT_FORMAT_ERROR',
+    AVDTP_BAD_RECOVERY_FORMAT_ERROR:        'AVDTP_BAD_RECOVERY_FORMAT_ERROR',
+    AVDTP_BAD_ROHC_FORMAT_ERROR:            'AVDTP_BAD_ROHC_FORMAT_ERROR',
+    AVDTP_BAD_CP_FORMAT_ERROR:              'AVDTP_BAD_CP_FORMAT_ERROR',
+    AVDTP_BAD_MULTIPLEXING_FORMAT_ERROR:    'AVDTP_BAD_MULTIPLEXING_FORMAT_ERROR',
+    AVDTP_UNSUPPORTED_CONFIGURATION_ERROR:  'AVDTP_UNSUPPORTED_CONFIGURATION_ERROR',
+    AVDTP_BAD_STATE_ERROR:                  'AVDTP_BAD_STATE_ERROR'
+}
+
+AVDTP_AUDIO_MEDIA_TYPE      = 0x00
+AVDTP_VIDEO_MEDIA_TYPE      = 0x01
+AVDTP_MULTIMEDIA_MEDIA_TYPE = 0x02
+
+AVDTP_MEDIA_TYPE_NAMES = {
+    AVDTP_AUDIO_MEDIA_TYPE:      'AVDTP_AUDIO_MEDIA_TYPE',
+    AVDTP_VIDEO_MEDIA_TYPE:      'AVDTP_VIDEO_MEDIA_TYPE',
+    AVDTP_MULTIMEDIA_MEDIA_TYPE: 'AVDTP_MULTIMEDIA_MEDIA_TYPE'
+}
+
+# TSEP (AVDTP spec - 8.20.3 Stream End-point Type, Source or Sink (TSEP))
+AVDTP_TSEP_SRC = 0x00
+AVDTP_TSEP_SNK = 0x01
+
+AVDTP_TSEP_NAMES = {
+    AVDTP_TSEP_SRC: 'AVDTP_TSEP_SRC',
+    AVDTP_TSEP_SNK: 'AVDTP_TSEP_SNK'
+}
+
+# Service Categories (AVDTP spec - Table 8.47: Service Category information element field values)
+AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY    = 0x01
+AVDTP_REPORTING_SERVICE_CATEGORY          = 0x02
+AVDTP_RECOVERY_SERVICE_CATEGORY           = 0x03
+AVDTP_CONTENT_PROTECTION_SERVICE_CATEGORY = 0x04
+AVDTP_HEADER_COMPRESSION_SERVICE_CATEGORY = 0x05
+AVDTP_MULTIPLEXING_SERVICE_CATEGORY       = 0x06
+AVDTP_MEDIA_CODEC_SERVICE_CATEGORY        = 0x07
+AVDTP_DELAY_REPORTING_SERVICE_CATEGORY    = 0x08
+
+AVDTP_SERVICE_CATEGORY_NAMES = {
+    AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY:    'AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY',
+    AVDTP_REPORTING_SERVICE_CATEGORY:          'AVDTP_REPORTING_SERVICE_CATEGORY',
+    AVDTP_RECOVERY_SERVICE_CATEGORY:           'AVDTP_RECOVERY_SERVICE_CATEGORY',
+    AVDTP_CONTENT_PROTECTION_SERVICE_CATEGORY: 'AVDTP_CONTENT_PROTECTION_SERVICE_CATEGORY',
+    AVDTP_HEADER_COMPRESSION_SERVICE_CATEGORY: 'AVDTP_HEADER_COMPRESSION_SERVICE_CATEGORY',
+    AVDTP_MULTIPLEXING_SERVICE_CATEGORY:       'AVDTP_MULTIPLEXING_SERVICE_CATEGORY',
+    AVDTP_MEDIA_CODEC_SERVICE_CATEGORY:        'AVDTP_MEDIA_CODEC_SERVICE_CATEGORY',
+    AVDTP_DELAY_REPORTING_SERVICE_CATEGORY:    'AVDTP_DELAY_REPORTING_SERVICE_CATEGORY'
+}
+
+# States (AVDTP spec - 9.1 State Definitions)
+AVDTP_IDLE_STATE       = 0x00
+AVDTP_CONFIGURED_STATE = 0x01
+AVDTP_OPEN_STATE       = 0x02
+AVDTP_STREAMING_STATE  = 0x03
+AVDTP_CLOSING_STATE    = 0x04
+AVDTP_ABORTING_STATE   = 0x05
+
+AVDTP_STATE_NAMES = {
+    AVDTP_IDLE_STATE:       'AVDTP_IDLE_STATE',
+    AVDTP_CONFIGURED_STATE: 'AVDTP_CONFIGURED_STATE',
+    AVDTP_OPEN_STATE:       'AVDTP_OPEN_STATE',
+    AVDTP_STREAMING_STATE:  'AVDTP_STREAMING_STATE',
+    AVDTP_CLOSING_STATE:    'AVDTP_CLOSING_STATE',
+    AVDTP_ABORTING_STATE:   'AVDTP_ABORTING_STATE'
+}
+
+
+# -----------------------------------------------------------------------------
+async def find_avdtp_service_with_sdp_client(sdp_client):
+    '''
+    Find an AVDTP service, using a connected SDP client, and return its version,
+    or None if none is found
+    '''
+
+    # Search for services with an Audio Sink service class
+    search_result = await sdp_client.search_attributes(
+        [BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE],
+        [
+            sdp.SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+        ]
+    )
+    for attribute_list in search_result:
+        profile_descriptor_list = sdp.ServiceAttribute.find_attribute_in_list(
+            attribute_list,
+            sdp.SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+        )
+        if profile_descriptor_list:
+            for profile_descriptor in profile_descriptor_list.value:
+                if len(profile_descriptor.value) >= 2:
+                    avdtp_version_major = profile_descriptor.value[1].value >> 8
+                    avdtp_version_minor = profile_descriptor.value[1].value & 0xFF
+                    return (avdtp_version_major, avdtp_version_minor)
+
+
+# -----------------------------------------------------------------------------
+async def find_avdtp_service_with_connection(device, connection):
+    '''
+    Find an AVDTP service, for a connection, and return its version,
+    or None if none is found
+    '''
+
+    sdp_client = sdp.Client(device)
+    await sdp_client.connect(connection)
+    service_version = await find_avdtp_service_with_sdp_client(sdp_client)
+    await sdp_client.disconnect()
+
+    return service_version
+
+
+# -----------------------------------------------------------------------------
+class RealtimeClock:
+    def now(self):
+        return time.time()
+
+    async def sleep(self, duration):
+        await asyncio.sleep(duration)
+
+
+# -----------------------------------------------------------------------------
+class MediaPacket:
+    @staticmethod
+    def from_bytes(data):
+        version         = (data[0] >> 6) & 0x03
+        padding         = (data[0] >> 5) & 0x01
+        extension       = (data[0] >> 4) & 0x01
+        csrc_count      = data[0] & 0x0F
+        marker          = (data[1] >> 7) & 0x01
+        payload_type    = data[1] & 0x7F
+        sequence_number = struct.unpack_from('>H', data, 2)[0]
+        timestamp       = struct.unpack_from('>I', data, 4)[0]
+        ssrc            = struct.unpack_from('>I', data, 8)[0]
+        csrc_list       = [struct.unpack_from('>I', data, 12 + i)[0] for i in range(csrc_count)]
+        payload         = data[12 + csrc_count * 4:]
+
+        return MediaPacket(
+            version,
+            padding,
+            extension,
+            marker,
+            sequence_number,
+            timestamp,
+            ssrc,
+            csrc_list,
+            payload_type,
+            payload
+        )
+
+    def __init__(
+        self,
+        version,
+        padding,
+        extension,
+        marker,
+        sequence_number,
+        timestamp,
+        ssrc,
+        csrc_list,
+        payload_type,
+        payload
+    ):
+        self.version         = version
+        self.padding         = padding
+        self.extension       = extension
+        self.marker          = marker
+        self.sequence_number = sequence_number
+        self.timestamp       = timestamp
+        self.ssrc            = ssrc
+        self.csrc_list       = csrc_list
+        self.payload_type    = payload_type
+        self.payload         = payload
+
+    def __bytes__(self):
+        header = (
+            bytes([
+                self.version << 6 | self.padding << 5 | self.extension << 4 | len(self.csrc_list),
+                self.marker << 7 | self.payload_type
+            ]) +
+            struct.pack('>HII', self.sequence_number, self.timestamp, self.ssrc)
+        )
+        for csrc in self.csrc_list:
+            header += struct.pack('>I', csrc)
+        return header + self.payload
+
+    def __str__(self):
+        return f'RTP(v={self.version},p={self.padding},x={self.extension},m={self.marker},pt={self.payload_type},sn={self.sequence_number},ts={self.timestamp},ssrc={self.ssrc},csrcs={self.csrc_list},payload_size={len(self.payload)})'
+
+
+# -----------------------------------------------------------------------------
+class MediaPacketPump:
+    def __init__(self, packets, clock=RealtimeClock()):
+        self.packets   = packets
+        self.clock     = clock
+        self.pump_task = None
+
+    async def start(self, rtp_channel):
+        async def pump_packets():
+            start_time      = 0
+            start_timestamp = 0
+
+            try:
+                logger.debug('pump starting')
+                async for packet in self.packets:
+                    # Capture the timestamp of the first packet
+                    if start_time == 0:
+                        start_time      = self.clock.now()
+                        start_timestamp = packet.timestamp_seconds
+
+                    # Wait until we can send
+                    when = start_time + (packet.timestamp_seconds - start_timestamp)
+                    now = self.clock.now()
+                    if when > now:
+                        delay = when - now
+                        logger.debug(f'waiting for {delay}')
+                        await self.clock.sleep(delay)
+
+                    # Emit
+                    rtp_channel.send_pdu(bytes(packet))
+                    logger.debug(f'{color(">>> sending RTP packet:", "green")} {packet}')
+            except asyncio.exceptions.CancelledError:
+                logger.debug('pump canceled')
+
+        # Pump packets
+        self.pump_task = asyncio.get_running_loop().create_task(pump_packets())
+
+    async def stop(self):
+        # Stop the pump
+        if self.pump_task:
+            self.pump_task.cancel()
+            await self.pump_task
+            self.pump_task = None
+
+
+# -----------------------------------------------------------------------------
+class MessageAssembler:
+    def __init__(self, callback):
+        self.callback = callback
+        self.reset()
+
+    def reset(self):
+        self.transaction_label        = 0
+        self.message                  = None
+        self.message_type             = 0
+        self.signal_identifier        = 0
+        self.number_of_signal_packets = 0
+        self.packet_count             = 0
+
+    def on_pdu(self, pdu):
+        self.packet_count += 1
+
+        transaction_label = pdu[0] >> 4
+        packet_type       = (pdu[0] >> 2) & 3
+        message_type      = pdu[0] & 3
+
+        logger.debug(f'transaction_label={transaction_label}, packet_type={Protocol.packet_type_name(packet_type)}, message_type={Message.message_type_name(message_type)}')
+        if packet_type == Protocol.SINGLE_PACKET or packet_type == Protocol.START_PACKET:
+            if self.message is not None:
+                # The previous message has not been terminated
+                logger.warning('received a start or single packet when expecting an end or continuation')
+                self.reset()
+
+            self.transaction_label = transaction_label
+            self.signal_identifier = pdu[1] & 0x3F
+            self.message_type      = message_type
+
+            if packet_type == Protocol.SINGLE_PACKET:
+                self.message = pdu[2:]
+                self.on_message_complete()
+            else:
+                self.number_of_signal_packets = pdu[2]
+                self.message                  = pdu[3:]
+        elif packet_type == Protocol.CONTINUE_PACKET or packet_type == Protocol.END_PACKET:
+            if self.packet_count == 0:
+                logger.warning('unexpected continuation')
+                return
+
+            if transaction_label != self.transaction_label:
+                logger.warning(f'transaction label mismatch: expected {self.transaction_label}, received {transaction_label}')
+                return
+
+            if message_type != self.message_type:
+                logger.warning(f'message type mismatch: expected {self.message_type}, received {message_type}')
+                return
+
+            self.message += pdu[1:]
+
+            if packet_type == Protocol.END_PACKET:
+                if self.packet_count != self.number_of_signal_packets:
+                    logger.warning(f'incomplete fragmented message: expected {self.number_of_signal_packets} packets, received {self.packet_count}')
+                    self.reset()
+                    return
+
+                self.on_message_complete()
+            else:
+                if self.packet_count > self.number_of_signal_packets:
+                    logger.warning(f'too many packets: expected {self.number_of_signal_packets}, received {self.packet_count}')
+                    self.reset()
+                    return
+
+    def on_message_complete(self):
+        message = Message.create(self.signal_identifier, self.message_type, self.message)
+
+        try:
+            self.callback(self.transaction_label, message)
+        except Exception as error:
+            logger.warning(color(f'!!! exception in callback: {error}'))
+
+        self.reset()
+
+
+# -----------------------------------------------------------------------------
+class ServiceCapabilities:
+    @staticmethod
+    def create(service_category, service_capabilities_bytes):
+        # Select the appropriate subclass
+        if service_category == AVDTP_MEDIA_CODEC_SERVICE_CATEGORY:
+            cls = MediaCodecCapabilities
+        else:
+            cls = ServiceCapabilities
+
+        # Create an instance and initialize it
+        instance = cls.__new__(cls)
+        instance.service_category = service_category
+        instance.service_capabilities_bytes = service_capabilities_bytes
+        instance.init_from_bytes()
+
+        return instance
+
+    @staticmethod
+    def parse_capabilities(payload):
+        capabilities = []
+        while payload:
+            service_category               = payload[0]
+            length_of_service_capabilities = payload[1]
+            service_capabilities_bytes     = payload[2:2 + length_of_service_capabilities]
+            capabilities.append(ServiceCapabilities.create(service_category, service_capabilities_bytes))
+
+            payload = payload[2 + length_of_service_capabilities:]
+
+        return capabilities
+
+    @staticmethod
+    def serialize_capabilities(capabilities):
+        serialized = b''
+        for item in capabilities:
+            serialized += bytes([
+                item.service_category,
+                len(item.service_capabilities_bytes)
+            ]) + item.service_capabilities_bytes
+        return serialized
+
+    def init_from_bytes(self):
+        pass
+
+    def __init__(self, service_category, service_capabilities_bytes=b''):
+        self.service_category           = service_category
+        self.service_capabilities_bytes = service_capabilities_bytes
+
+    def to_string(self, details=[]):
+        attributes = ','.join([name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)] + details)
+        return f'ServiceCapabilities({attributes})'
+
+    def __str__(self):
+        if self.service_capabilities_bytes:
+            details = [self.service_capabilities_bytes.hex()]
+        else:
+            details = []
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+class MediaCodecCapabilities(ServiceCapabilities):
+    def init_from_bytes(self):
+        self.media_type              = self.service_capabilities_bytes[0]
+        self.media_codec_type        = self.service_capabilities_bytes[1]
+        self.media_codec_information = self.service_capabilities_bytes[2:]
+
+        if self.media_codec_type == A2DP_SBC_CODEC_TYPE:
+            self.media_codec_information = SbcMediaCodecInformation.from_bytes(self.media_codec_information)
+        elif self.media_codec_type == A2DP_MPEG_2_4_AAC_CODEC_TYPE:
+            self.media_codec_information = AacMediaCodecInformation.from_bytes(self.media_codec_information)
+        elif self.media_codec_type == A2DP_NON_A2DP_CODEC_TYPE:
+            self.media_codec_information = VendorSpecificMediaCodecInformation.from_bytes(self.media_codec_information)
+
+    def __init__(self, media_type, media_codec_type, media_codec_information):
+        super().__init__(
+            AVDTP_MEDIA_CODEC_SERVICE_CATEGORY,
+            bytes([media_type, media_codec_type]) + bytes(media_codec_information)
+        )
+        self.media_type              = media_type
+        self.media_codec_type        = media_codec_type
+        self.media_codec_information = media_codec_information
+
+    def __str__(self):
+        details = [
+            f'media_type={name_or_number(AVDTP_MEDIA_TYPE_NAMES, self.media_type)}',
+            f'codec={name_or_number(A2DP_CODEC_TYPE_NAMES, self.media_codec_type)}',
+            f'codec_info={self.media_codec_information.hex() if type(self.media_codec_information) is bytes else str(self.media_codec_information)}'
+        ]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+class EndPointInfo:
+    @staticmethod
+    def from_bytes(payload):
+        return EndPointInfo(
+            payload[0] >> 2,
+            payload[0] >> 1 & 1,
+            payload[1] >> 4,
+            payload[1] >> 3 & 1
+        )
+
+    def __bytes__(self):
+        return bytes([
+            self.seid << 2 | self.in_use << 1,
+            self.media_type << 4 | self.tsep << 3
+        ])
+
+    def __init__(self, seid, in_use, media_type, tsep):
+        self.seid       = seid
+        self.in_use     = in_use
+        self.media_type = media_type
+        self.tsep       = tsep
+
+
+# -----------------------------------------------------------------------------
+class Message:
+    COMMAND         = 0
+    GENERAL_REJECT  = 1
+    RESPONSE_ACCEPT = 2
+    RESPONSE_REJECT = 3
+
+    MESSAGE_TYPE_NAMES = {
+        COMMAND:         'COMMAND',
+        GENERAL_REJECT:  'GENERAL_REJECT',
+        RESPONSE_ACCEPT: 'RESPONSE_ACCEPT',
+        RESPONSE_REJECT: 'RESPONSE_REJECT'
+    }
+
+    subclasses = {}  # Subclasses, by signal identifier and message type
+
+    @staticmethod
+    def message_type_name(message_type):
+        return name_or_number(Message.MESSAGE_TYPE_NAMES, message_type)
+
+    @staticmethod
+    def subclass(cls):
+        # Infer the signal identifier and message subtype from the class name
+        name = cls.__name__
+        if name == 'General_Reject':
+            cls.signal_identifier = 0
+            signal_identifier_str = None
+            message_type = Message.COMMAND
+        elif name.endswith('_Command'):
+            signal_identifier_str = name[:-8]
+            message_type = Message.COMMAND
+        elif name.endswith('_Response'):
+            signal_identifier_str = name[:-9]
+            message_type = Message.RESPONSE_ACCEPT
+        elif name.endswith('_Reject'):
+            signal_identifier_str = name[:-7]
+            message_type = Message.RESPONSE_REJECT
+        else:
+            raise ValueError('invalid class name')
+
+        cls.message_type = message_type
+
+        if signal_identifier_str is not None:
+            for (name, signal_identifier) in AVDTP_SIGNAL_IDENTIFIERS.items():
+                if name.lower().endswith(signal_identifier_str.lower()):
+                    cls.signal_identifier = signal_identifier
+                    break
+
+            # Register the subclass
+            Message.subclasses.setdefault(cls.signal_identifier, {})[cls.message_type] = cls
+
+        return cls
+
+    # Factory method to create a subclass based on the signal identifier and message type
+    @staticmethod
+    def create(signal_identifier, message_type, payload):
+        # Look for a registered subclass
+        subclasses = Message.subclasses.get(signal_identifier)
+        if subclasses:
+            subclass = subclasses.get(message_type)
+            if subclass:
+                instance = subclass.__new__(subclass)
+                instance.payload = payload
+                instance.init_from_payload()
+                return instance
+
+        # Instantiate the appropriate class based on the message type
+        if message_type == Message.RESPONSE_REJECT:
+            # Assume a simple reject message
+            instance = Simple_Reject(payload)
+            instance.init_from_payload()
+        else:
+            instance = Message(payload)
+        instance.signal_identifier = signal_identifier
+        instance.message_type = message_type
+        return instance
+
+    def init_from_payload(self):
+        pass
+
+    def __init__(self, payload=b''):
+        self.payload           = payload
+
+    def to_string(self, details):
+        base = f'{color(f"{name_or_number(AVDTP_SIGNAL_NAMES, self.signal_identifier)}_{Message.message_type_name(self.message_type)}", "yellow")}'
+        if details:
+            if type(details) is str:
+                return f'{base}: {details}'
+            else:
+                return base + ':\n' + '\n'.join(['  ' + color(detail, 'cyan') for detail in details])
+        else:
+            return base
+
+    def __str__(self):
+        return self.to_string(self.payload.hex())
+
+
+# -----------------------------------------------------------------------------
+class Simple_Command(Message):
+    '''
+    Command message with just one seid
+    '''
+
+    def init_from_payload(self):
+        self.acp_seid = self.payload[0] >> 2
+
+    def __init__(self, seid):
+        self.acp_seid = seid
+        self.payload = bytes([seid << 2])
+
+    def __str__(self):
+        return self.to_string([f'ACP SEID: {self.acp_seid}'])
+
+
+# -----------------------------------------------------------------------------
+class Simple_Reject(Message):
+    '''
+    Reject messages with just an error code
+    '''
+
+    def init_from_payload(self):
+        self.error_code = self.payload[0]
+
+    def __init__(self, error_code):
+        self.error_code = error_code
+        self.payload = bytes([self.error_code])
+
+    def __str__(self):
+        details = [
+            f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}'
+        ]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Discover_Command(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.6.1 Stream End Point Discovery Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Discover_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.6.2 Stream End Point Discovery Response
+    '''
+
+    def init_from_payload(self):
+        self.endpoints = []
+        endpoint_count = len(self.payload) // 2
+        for i in range(endpoint_count):
+            self.endpoints.append(EndPointInfo.from_bytes(self.payload[i * 2:(i + 1) * 2]))
+
+    def __init__(self, endpoints):
+        self.endpoints = endpoints
+        self.payload   = b''.join([bytes(endpoint) for endpoint in endpoints])
+
+    def __str__(self):
+        details = []
+        for endpoint in self.endpoints:
+            details.extend(
+                [
+                    f'ACP SEID: {endpoint.seid}',
+                    f'  in_use:     {endpoint.in_use}',
+                    f'  media_type: {name_or_number(AVDTP_MEDIA_TYPE_NAMES, endpoint.media_type)}',
+                    f'  tsep:       {name_or_number(AVDTP_TSEP_NAMES, endpoint.tsep)}'
+                ]
+            )
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_Capabilities_Command(Simple_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.7.1 Get Capabilities Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_Capabilities_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.7.2 Get All Capabilities Response
+    '''
+
+    def init_from_payload(self):
+        self.capabilities = ServiceCapabilities.parse_capabilities(self.payload)
+
+    def __init__(self, capabilities):
+        self.capabilities = capabilities
+        self.payload = ServiceCapabilities.serialize_capabilities(capabilities)
+
+    def __str__(self):
+        details = [str(capability) for capability in self.capabilities]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_Capabilities_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.7.3 Get Capabilities Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_All_Capabilities_Command(Get_Capabilities_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.8.1 Get All Capabilities Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_All_Capabilities_Response(Get_Capabilities_Response):
+    '''
+    See Bluetooth AVDTP spec - 8.8.2 Get All Capabilities Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_All_Capabilities_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.8.3 Get All Capabilities Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Set_Configuration_Command(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.9.1 Set Configuration Command
+    '''
+
+    def init_from_payload(self):
+        self.acp_seid     = self.payload[0] >> 2
+        self.int_seid     = self.payload[1] >> 2
+        self.capabilities = ServiceCapabilities.parse_capabilities(self.payload[2:])
+
+    def __init__(self, acp_seid, int_seid, capabilities):
+        self.acp_seid     = acp_seid
+        self.int_seid     = int_seid
+        self.capabilities = capabilities
+        self.payload      = bytes([acp_seid << 2, int_seid << 2]) + ServiceCapabilities.serialize_capabilities(capabilities)
+
+    def __str__(self):
+        details = [
+            f'ACP SEID: {self.acp_seid}',
+            f'INT SEID: {self.int_seid}'
+        ] + [str(capability) for capability in self.capabilities]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Set_Configuration_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.9.2 Set Configuration Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Set_Configuration_Reject(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.9.3 Set Configuration Reject
+    '''
+
+    def init_from_payload(self):
+        self.service_category = self.payload[0]
+        self.error_code       = self.payload[1]
+
+    def __init__(self, service_category, error_code):
+        self.service_category = service_category
+        self.error_code       = error_code
+        self.payload          = bytes([service_category, self.error_code])
+
+    def __str__(self):
+        details = [
+            f'service_category: {name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)}',
+            f'error_code:       {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}'
+        ]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_Configuration_Command(Simple_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.10.1 Get Configuration Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_Configuration_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.10.2 Get Configuration Response
+    '''
+
+    def init_from_payload(self):
+        self.capabilities = ServiceCapabilities.parse_capabilities(self.payload)
+
+    def __init__(self, capabilities):
+        self.capabilities = capabilities
+        self.payload = ServiceCapabilities.serialize_capabilities(capabilities)
+
+    def __str__(self):
+        details = [str(capability) for capability in self.capabilities]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Get_Configuration_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.10.3 Get Configuration Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Reconfigure_Command(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.11.1 Reconfigure Command
+    '''
+
+    def init_from_payload(self):
+        self.acp_seid     = self.payload[0] >> 2
+        self.capabilities = ServiceCapabilities.parse_capabilities(self.payload[1:])
+
+    def __str__(self):
+        details = [
+            f'ACP SEID: {self.acp_seid}',
+        ] + [str(capability) for capability in self.capabilities]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Reconfigure_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.11.2 Reconfigure Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Reconfigure_Reject(Set_Configuration_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.11.3 Reconfigure Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Open_Command(Simple_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.12.1 Open Stream Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Open_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.12.2 Open Stream Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Open_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.12.3 Open Stream Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Start_Command(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.13.1 Start Stream Command
+    '''
+
+    def init_from_payload(self):
+        self.acp_seids = [x >> 2 for x in self.payload]
+
+    def __init__(self, seids):
+        self.acp_seids = seids
+        self.payload = bytes([seid << 2 for seid in self.acp_seids])
+
+    def __str__(self):
+        return self.to_string([f'ACP SEIDs: {self.acp_seids}'])
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Start_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.13.2 Start Stream Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Start_Reject(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.13.3 Set Configuration Reject
+    '''
+
+    def init_from_payload(self):
+        self.acp_seid   = self.payload[0] >> 2
+        self.error_code = self.payload[1]
+
+    def __init__(self, acp_seid, error_code):
+        self.acp_seid   = acp_seid
+        self.error_code = error_code
+        self.payload    = bytes([self.acp_seid << 2, self.error_code])
+
+    def __str__(self):
+        details = [
+            f'acp_seid:   {self.acp_seid}',
+            f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}'
+        ]
+        return self.to_string(details)
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Close_Command(Simple_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.14.1 Close Stream Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Close_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.14.2 Close Stream Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Close_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.14.3 Close Stream Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Suspend_Command(Start_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.15.1 Suspend Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Suspend_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.15.2 Suspend Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Suspend_Reject(Start_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.15.3 Suspend Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Abort_Command(Simple_Command):
+    '''
+    See Bluetooth AVDTP spec - 8.16.1 Abort Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Abort_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.16.2 Abort Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Security_Control_Command(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.17.1 Security Control Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Security_Control_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.17.2 Security Control Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class Security_Control_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.17.3 Security Control Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class General_Reject(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.18 General Reject
+    '''
+
+    def to_string(self, details):
+        return f'{color(f"GENERAL_REJECT", "yellow")}'
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class DelayReport_Command(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.19.1 Delay Report Command
+    '''
+
+    def init_from_payload(self):
+        self.acp_seid = self.payload[0] >> 2
+        self.delay    = (self.payload[1] << 8) | (self.payload[2])
+
+    def __str__(self):
+        return self.to_string([
+            f'ACP_SEID: {self.acp_seid}',
+            f'delay:    {self.delay}'
+        ])
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class DelayReport_Response(Message):
+    '''
+    See Bluetooth AVDTP spec - 8.19.2 Delay Report Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@Message.subclass
+class DelayReport_Reject(Simple_Reject):
+    '''
+    See Bluetooth AVDTP spec - 8.19.3 Delay Report Reject
+    '''
+
+
+# -----------------------------------------------------------------------------
+class Protocol:
+    SINGLE_PACKET   = 0
+    START_PACKET    = 1
+    CONTINUE_PACKET = 2
+    END_PACKET      = 3
+
+    PACKET_TYPE_NAMES = {
+        SINGLE_PACKET:   'SINGLE_PACKET',
+        START_PACKET:    'START_PACKET',
+        CONTINUE_PACKET: 'CONTINUE_PACKET',
+        END_PACKET:      'END_PACKET'
+    }
+
+    @staticmethod
+    def packet_type_name(packet_type):
+        return name_or_number(Protocol.PACKET_TYPE_NAMES, packet_type)
+
+    @staticmethod
+    async def connect(connection, version=(1, 3)):
+        connector = connection.create_l2cap_connector(AVDTP_PSM)
+        channel = await connector()
+        protocol = Protocol(channel, version)
+        protocol.channel_connector = connector
+
+        return protocol
+
+    def __init__(self, l2cap_channel, version=(1, 3)):
+        self.l2cap_channel         = l2cap_channel
+        self.version               = version
+        self.rtx_sig_timer         = AVDTP_DEFAULT_RTX_SIG_TIMER
+        self.message_assembler     = MessageAssembler(self.on_message)
+        self.transaction_results   = [None] * 16  # Futures for up to 16 transactions
+        self.transaction_semaphore = asyncio.Semaphore(16)
+        self.transaction_count     = 0
+        self.channel_acceptor      = None
+        self.channel_connector     = None
+        self.local_endpoints       = []  # Local endpoints, with contiguous seid values
+        self.remote_endpoints      = {}  # Remote stream endpoints, by seid
+        self.streams               = {}  # Streams, by seid
+
+        # Register to receive PDUs from the channel
+        l2cap_channel.sink = self.on_pdu
+        l2cap_channel.on('open', self.on_l2cap_channel_open)
+
+    def get_local_endpoint_by_seid(self, seid):
+        if seid > 0 and seid <= len(self.local_endpoints):
+            return self.local_endpoints[seid - 1]
+
+    def add_source(self, codec_capabilities, packet_pump):
+        seid = len(self.local_endpoints) + 1
+        source = LocalSource(self, seid, codec_capabilities, packet_pump)
+        self.local_endpoints.append(source)
+
+        return source
+
+    def add_sink(self, codec_capabilities):
+        seid = len(self.local_endpoints) + 1
+        sink = LocalSink(self, seid, codec_capabilities)
+        self.local_endpoints.append(sink)
+
+        return sink
+
+    async def create_stream(self, source, sink):
+        # Check that the source isn't already used in a stream
+        if source.in_use:
+            raise InvalidStateError('source already in use')
+
+        # Create or reuse a new stream to associate the source and the sink
+        if source.seid in self.streams:
+            stream = self.streams[source.seid]
+        else:
+            stream = Stream(self, source, sink)
+            self.streams[source.seid] = stream
+
+        # The stream can now be configured
+        await stream.configure()
+
+        return stream
+
+    async def discover_remote_endpoints(self):
+        self.remote_endpoints = {}
+
+        response = await self.send_command(Discover_Command())
+        for endpoint_entry in response.endpoints:
+            logger.debug(f'getting endpoint capabilities for endpoint {endpoint_entry.seid}')
+            get_capabilities_response = await self.get_capabilities(endpoint_entry.seid)
+            endpoint = DiscoveredStreamEndPoint(
+                self,
+                endpoint_entry.seid,
+                endpoint_entry.media_type,
+                endpoint_entry.tsep,
+                endpoint_entry.in_use,
+                get_capabilities_response.capabilities
+            )
+            self.remote_endpoints[endpoint_entry.seid] = endpoint
+
+        return self.remote_endpoints.values()
+
+    def find_remote_sink_by_codec(self, media_type, codec_type):
+        for endpoint in self.remote_endpoints.values():
+            if not endpoint.in_use and endpoint.media_type == media_type and endpoint.tsep == AVDTP_TSEP_SNK:
+                has_media_transport = False
+                has_codec = False
+                for capabilities in endpoint.capabilities:
+                    if capabilities.service_category == AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY:
+                        has_media_transport = True
+                    elif capabilities.service_category == AVDTP_MEDIA_CODEC_SERVICE_CATEGORY:
+                        if capabilities.media_type == AVDTP_AUDIO_MEDIA_TYPE and capabilities.media_codec_type == codec_type:
+                            has_codec = True
+                if has_media_transport and has_codec:
+                    return endpoint
+
+    def on_pdu(self, pdu):
+        self.message_assembler.on_pdu(pdu)
+
+    def on_message(self, transaction_label, message):
+        logger.debug(f'{color("<<< Received AVDTP message", "magenta")}: [{transaction_label}] {message}')
+
+        # Check that the identifier is not reserved
+        if message.signal_identifier == 0:
+            logger.warning('!!! reserved signal identifier')
+            return
+
+        # Check that the identifier is valid
+        if message.signal_identifier < 0 or message.signal_identifier > AVDTP_DELAYREPORT:
+            logger.warning('!!! invalid signal identifier')
+            self.send_message(transaction_label, General_Reject())
+
+        if message.message_type == Message.COMMAND:
+            # Command
+            handler_name = f'on_{AVDTP_SIGNAL_NAMES.get(message.signal_identifier,"").replace("AVDTP_","").lower()}_command'
+            handler = getattr(self, handler_name, None)
+            if handler:
+                try:
+                    response = handler(message)
+                    self.send_message(transaction_label, response)
+                except Exception as error:
+                    logger.warning(f'{color("!!! Exception in handler:", "red")} {error}')
+            else:
+                logger.warning('unhandled command')
+        else:
+            # Response, look for a pending transaction with the same label
+            transaction_result = self.transaction_results[transaction_label]
+            if transaction_result is None:
+                logger.warning(color('!!! no pending transaction for label', 'red'))
+                return
+
+            transaction_result.set_result(message)
+            self.transaction_results[transaction_label] = None
+            self.transaction_semaphore.release()
+
+    def on_l2cap_connection(self, channel):
+        # Forward the channel to the endpoint that's expecting it
+        if self.channel_acceptor:
+            self.channel_acceptor.on_l2cap_connection(channel)
+
+    def on_l2cap_channel_open(self):
+        logger.debug(color('<<< L2CAP channel open', 'magenta'))
+
+    def send_message(self, transaction_label, message):
+        logger.debug(f'{color(">>> Sending AVDTP message", "magenta")}: [{transaction_label}] {message}')
+        max_fragment_size = self.l2cap_channel.mtu - 3  # Enough space for a 3-byte start packet header
+        payload = message.payload
+        if len(payload) + 2 <= self.l2cap_channel.mtu:
+            # Fits in a single packet
+            packet_type = self.SINGLE_PACKET
+        else:
+            packet_type = self.START_PACKET
+
+        done = False
+        while not done:
+            first_header_byte = transaction_label << 4 | packet_type << 2 | message.message_type
+
+            if packet_type == self.SINGLE_PACKET:
+                header = bytes([first_header_byte, message.signal_identifier])
+            elif packet_type == self.START_PACKET:
+                packet_count = (max_fragment_size - 1 + len(payload)) // max_fragment_size
+                header = bytes([first_header_byte, message.signal_identifier, packet_count])
+            else:
+                header = bytes([first_header_byte])
+
+            # Send one packet
+            self.l2cap_channel.send_pdu(header + payload[:max_fragment_size])
+
+            # Prepare for the next packet
+            payload = payload[max_fragment_size:]
+            if payload:
+                packet_type = self.CONTINUE_PACKET if payload > max_fragment_size else self.END_PACKET
+            else:
+                done = True
+
+    async def send_command(self, command):
+        # TODO: support timeouts
+        # Send the command
+        (transaction_label, transaction_result) = await self.start_transaction()
+        self.send_message(transaction_label, command)
+
+        # Wait for the response
+        response = await transaction_result
+
+        # Check for errors
+        if response.message_type == Message.GENERAL_REJECT or response.message_type == Message.RESPONSE_REJECT:
+            raise ProtocolError(response.error_code, 'avdtp')
+
+        return response
+
+    async def start_transaction(self):
+        # Wait until we can start a new transaction
+        await self.transaction_semaphore.acquire()
+
+        # Look for the next free entry to store the transaction result
+        for i in range(16):
+            transaction_label = (self.transaction_count + i) % 16
+            if self.transaction_results[transaction_label] is None:
+                transaction_result = asyncio.get_running_loop().create_future()
+                self.transaction_results[transaction_label] = transaction_result
+                self.transaction_count += 1
+                return (transaction_label, transaction_result)
+
+        assert(False)  # Should never reach this
+
+    async def get_capabilities(self, seid):
+        if self.version > (1, 2):
+            return await self.send_command(Get_All_Capabilities_Command(seid))
+        else:
+            return await self.send_command(Get_Capabilities_Command(seid))
+
+    async def set_configuration(self, acp_seid, int_seid, capabilities):
+        return await self.send_command(Set_Configuration_Command(acp_seid, int_seid, capabilities))
+
+    async def get_configuration(self, seid):
+        response = await self.send_command(Get_Configuration_Command(seid))
+        return response.capabilities
+
+    async def open(self, seid):
+        return await self.send_command(Open_Command(seid))
+
+    async def start(self, seids):
+        return await self.send_command(Start_Command(seids))
+
+    async def suspend(self, seids):
+        return await self.send_command(Suspend_Command(seids))
+
+    async def close(self, seid):
+        return await self.send_command(Close_Command(seid))
+
+    async def abort(self, seid):
+        return await self.send_command(Abort_Command(seid))
+
+    def on_discover_command(self, command):
+        endpoint_infos = [
+            EndPointInfo(endpoint.seid, 0, endpoint.media_type, endpoint.tsep)
+            for endpoint in self.local_endpoints
+        ]
+        return Discover_Response(endpoint_infos)
+
+    def on_get_capabilities_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Get_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+
+        return Get_Capabilities_Response(endpoint.capabilities)
+
+    def on_get_all_capabilities_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Get_All_Capabilities_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+
+        return Get_All_Capabilities_Response(endpoint.capabilities)
+
+    def on_set_configuration_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Set_Configuration_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+
+        # Check that the local endpoint isn't in use
+        if endpoint.in_use:
+            return Set_Configuration_Reject(AVDTP_SEP_IN_USE_ERROR)
+
+        # Create a stream object for the pair of endpoints
+        stream = Stream(self, endpoint, StreamEndPointProxy(self, command.int_seid))
+        self.streams[command.acp_seid] = stream
+
+        result = stream.on_set_configuration_command(command.capabilities)
+        return result or Set_Configuration_Response()
+
+    def on_get_configuration_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Get_Configuration_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+        if endpoint.stream is None:
+            return Get_Configuration_Reject(AVDTP_BAD_STATE_ERROR)
+
+        return endpoint.stream.on_get_configuration_command()
+
+    def on_reconfigure_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Reconfigure_Reject(0, AVDTP_BAD_ACP_SEID_ERROR)
+        if endpoint.stream is None:
+            return Reconfigure_Reject(0, AVDTP_BAD_STATE_ERROR)
+
+        result = endpoint.stream.on_reconfigure_command(command.capabilities)
+        return result or Reconfigure_Response()
+
+    def on_open_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Open_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+        if endpoint.stream is None:
+            return Open_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = endpoint.stream.on_open_command()
+        return result or Open_Response()
+
+    def on_start_command(self, command):
+        for seid in command.acp_seids:
+            endpoint = self.get_local_endpoint_by_seid(seid)
+            if endpoint is None:
+                return Start_Reject(seid, AVDTP_BAD_ACP_SEID_ERROR)
+            if endpoint.stream is None:
+                return Start_Reject(AVDTP_BAD_STATE_ERROR)
+
+        # Start all streams
+        # TODO: deal with partial failures
+        for seid in command.acp_seids:
+            endpoint = self.get_local_endpoint_by_seid(seid)
+            result = endpoint.stream.on_start_command()
+            if result is not None:
+                return result
+
+        return Start_Response()
+
+    def on_suspend_command(self, command):
+        for seid in command.acp_seids:
+            endpoint = self.get_local_endpoint_by_seid(seid)
+            if endpoint is None:
+                return Suspend_Reject(seid, AVDTP_BAD_ACP_SEID_ERROR)
+            if endpoint.stream is None:
+                return Suspend_Reject(seid, AVDTP_BAD_STATE_ERROR)
+
+        # Suspend all streams
+        # TODO: deal with partial failures
+        for seid in command.acp_seids:
+            endpoint = self.get_local_endpoint_by_seid(seid)
+            result = endpoint.stream.on_suspend_command()
+            if result is not None:
+                return result
+
+        return Suspend_Response()
+
+    def on_close_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Close_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+        if endpoint.stream is None:
+            return Close_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = endpoint.stream.on_close_command()
+        return result or Close_Response()
+
+    def on_abort_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None or endpoint.stream is None:
+            return Abort_Response()
+
+        endpoint.stream.on_abort_command()
+        return Abort_Response()
+
+    def on_security_control_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return Security_Control_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+
+        result = endpoint.on_security_control_command(command.payload)
+        return result or Security_Control_Response()
+
+    def on_delayreport_command(self, command):
+        endpoint = self.get_local_endpoint_by_seid(command.acp_seid)
+        if endpoint is None:
+            return DelayReport_Reject(AVDTP_BAD_ACP_SEID_ERROR)
+
+        result = endpoint.on_delayreport_command(command.delay)
+        return result or DelayReport_Response()
+
+
+# -----------------------------------------------------------------------------
+class Listener(EventEmitter):
+    @staticmethod
+    def create_registrar(device):
+        return device.create_l2cap_registrar(AVDTP_PSM)
+
+    def set_server(self, connection, server):
+        self.servers[connection.handle] = server
+
+    def __init__(self, registrar, version=(1, 3)):
+        super().__init__()
+        self.version = version
+        self.servers = {}  # Servers, by connection handle
+
+        # Listen for incoming L2CAP connections
+        registrar(self.on_l2cap_connection)
+
+    def on_l2cap_connection(self, channel):
+        logger.debug(f'{color("<<< incoming L2CAP connection:", "magenta")} {channel}')
+
+        if channel.connection.handle in self.servers:
+            # This is a channel for a stream endpoint
+            server = self.servers[channel.connection.handle]
+            server.on_l2cap_connection(channel)
+        else:
+            # This is a new command/response channel
+            def on_channel_open():
+                server = Protocol(channel, self.version)
+                self.set_server(channel.connection, server)
+                self.emit('connection', server)
+            channel.on('open', on_channel_open)
+
+
+# -----------------------------------------------------------------------------
+class Stream:
+    '''
+    Pair of a local and a remote stream endpoint that can stream from one to the other
+    '''
+
+    @staticmethod
+    def state_name(state):
+        return name_or_number(AVDTP_STATE_NAMES, state)
+
+    def change_state(self, state):
+        logger.debug(f'{self} state change -> {color(self.state_name(state), "cyan")}')
+        self.state = state
+
+    def send_media_packet(self, packet):
+        self.rtp_channel.send_pdu(bytes(packet))
+
+    async def configure(self):
+        if self.state != AVDTP_IDLE_STATE:
+            raise InvalidStateError('current state is not IDLE')
+
+        await self.remote_endpoint.set_configuration(
+            self.local_endpoint.seid,
+            self.local_endpoint.configuration
+        )
+        self.change_state(AVDTP_CONFIGURED_STATE)
+
+    async def open(self):
+        if self.state != AVDTP_CONFIGURED_STATE:
+            raise InvalidStateError('current state is not CONFIGURED')
+
+        logger.debug('opening remote endpoint')
+        await self.remote_endpoint.open()
+
+        self.change_state(AVDTP_OPEN_STATE)
+
+        # Create a channel for RTP packets
+        self.rtp_channel = await self.protocol.channel_connector()
+
+    async def start(self):
+        # Auto-open if needed
+        if self.state == AVDTP_CONFIGURED_STATE:
+            await self.open()
+
+        if self.state != AVDTP_OPEN_STATE:
+            raise InvalidStateError('current state is not OPEN')
+
+        logger.debug('starting remote endpoint')
+        await self.remote_endpoint.start()
+
+        logger.debug('starting local endpoint')
+        await self.local_endpoint.start()
+
+        self.change_state(AVDTP_STREAMING_STATE)
+
+    async def stop(self):
+        if self.state != AVDTP_STREAMING_STATE:
+            raise InvalidStateError('current state is not STREAMING')
+
+        logger.debug('stopping local endpoint')
+        await self.local_endpoint.stop()
+
+        logger.debug('stopping remote endpoint')
+        await self.remote_endpoint.stop()
+
+        self.change_state(AVDTP_OPEN_STATE)
+
+    async def close(self):
+        if self.state not in {AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE}:
+            raise InvalidStateError('current state is not OPEN or STREAMING')
+
+        logger.debug('closing local endpoint')
+        await self.local_endpoint.close()
+
+        logger.debug('closing remote endpoint')
+        await self.remote_endpoint.close()
+
+        # Release any channels we may have created
+        self.change_state(AVDTP_CLOSING_STATE)
+        if self.rtp_channel:
+            await self.rtp_channel.disconnect()
+            self.rtp_channel = None
+
+        # Release the endpoint
+        self.local_endpoint.in_use = 0
+
+        self.change_state(AVDTP_IDLE_STATE)
+
+    def on_set_configuration_command(self, configuration):
+        if self.state != AVDTP_IDLE_STATE:
+            return Set_Configuration_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = self.local_endpoint.on_set_configuration_command(configuration)
+        if result is not None:
+            return result
+
+        self.change_state(AVDTP_CONFIGURED_STATE)
+
+    def on_get_configuration_command(self, configuration):
+        if self.state not in {AVDTP_CONFIGURED_STATE, AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE}:
+            return Get_Configuration_Reject(AVDTP_BAD_STATE_ERROR)
+
+        return self.local_endpoint.on_get_configuration_command(configuration)
+
+    def on_reconfigure_command(self, configuration):
+        if self.state != AVDTP_OPEN_STATE:
+            return Reconfigure_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = self.local_endpoint.on_reconfigure_command(configuration)
+        if result is not None:
+            return result
+
+    def on_open_command(self):
+        if self.state != AVDTP_CONFIGURED_STATE:
+            return Open_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = self.local_endpoint.on_open_command()
+        if result is not None:
+            return result
+
+        # Register to accept the next channel
+        self.protocol.channel_acceptor = self
+
+        self.change_state(AVDTP_OPEN_STATE)
+
+    def on_start_command(self):
+        if self.state != AVDTP_OPEN_STATE:
+            return Open_Reject(AVDTP_BAD_STATE_ERROR)
+
+        # Check that we have an RTP channel
+        if self.rtp_channel is None:
+            logger.warning('received start command before RTP channel establishment')
+            return Open_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = self.local_endpoint.on_start_command()
+        if result is not None:
+            return result
+
+        self.change_state(AVDTP_STREAMING_STATE)
+
+    def on_suspend_command(self):
+        if self.state != AVDTP_STREAMING_STATE:
+            return Open_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = self.local_endpoint.on_suspend_command()
+        if result is not None:
+            return result
+
+        self.change_state(AVDTP_OPEN_STATE)
+
+    def on_close_command(self):
+        if self.state not in {AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE}:
+            return Open_Reject(AVDTP_BAD_STATE_ERROR)
+
+        result = self.local_endpoint.on_close_command()
+        if result is not None:
+            return result
+
+        self.change_state(AVDTP_CLOSING_STATE)
+
+        if self.rtp_channel is None:
+            # No channel to release, we're done
+            self.change_state(AVDTP_IDLE_STATE)
+        else:
+            # TODO: set a timer as we wait for the RTP channel to be closed
+            pass
+
+    def on_abort_command(self):
+        if self.rtp_channel is None:
+            # No need to wait
+            self.change_state(AVDTP_IDLE_STATE)
+        else:
+            # Wait for the RTP channel to be closed
+            self.change_state(AVDTP_ABORTING_STATE)
+
+    def on_l2cap_connection(self, channel):
+        logger.debug(color('<<< stream channel connected', 'magenta'))
+        self.rtp_channel = channel
+        channel.on('open',  self.on_l2cap_channel_open)
+        channel.on('close', self.on_l2cap_channel_close)
+
+        # We don't need more channels
+        self.protocol.channel_acceptor = None
+
+    def on_l2cap_channel_open(self):
+        logger.debug(color('<<< stream channel open', 'magenta'))
+        self.local_endpoint.on_rtp_channel_open()
+
+    def on_l2cap_channel_close(self):
+        logger.debug(color('<<< stream channel closed', 'magenta'))
+        self.local_endpoint.on_rtp_channel_close()
+        self.local_endpoint.in_use = 0
+        self.rtp_channel = None
+
+        if self.state in {AVDTP_CLOSING_STATE, AVDTP_ABORTING_STATE}:
+            self.change_state(AVDTP_IDLE_STATE)
+        else:
+            logger.warning('unexpected channel close while not CLOSING or ABORTING')
+
+    def __init__(self, protocol, local_endpoint, remote_endpoint):
+        '''
+        remote_endpoint must be a subclass of StreamEndPointProxy
+
+        '''
+        self.protocol        = protocol
+        self.local_endpoint  = local_endpoint
+        self.remote_endpoint = remote_endpoint
+        self.rtp_channel     = None
+        self.state           = AVDTP_IDLE_STATE
+
+        local_endpoint.stream = self
+        local_endpoint.in_use = 1
+
+    def __str__(self):
+        return f'Stream({self.local_endpoint.seid} -> {self.remote_endpoint.seid} {self.state_name(self.state)})'
+
+
+# -----------------------------------------------------------------------------
+class StreamEndPoint:
+    def __init__(self, seid, media_type, tsep, in_use, capabilities):
+        self.seid         = seid
+        self.media_type   = media_type
+        self.tsep         = tsep
+        self.in_use       = in_use
+        self.capabilities = capabilities
+
+    def __str__(self):
+        return '\n'.join([
+            'SEP(',
+            f'  seid={self.seid}',
+            f'  media_type={name_or_number(AVDTP_MEDIA_TYPE_NAMES, self.media_type)}',
+            f'  tsep={name_or_number(AVDTP_TSEP_NAMES, self.tsep)}',
+            f'  in_use={self.in_use}',
+            '  capabilities=[',
+            '\n'.join([f'    {x}' for x in self.capabilities]),
+            '  ]',
+            ')'
+        ])
+
+
+# -----------------------------------------------------------------------------
+class StreamEndPointProxy:
+    def __init__(self, protocol, seid):
+        self.seid     = seid
+        self.protocol = protocol
+
+    async def set_configuration(self, int_seid, configuration):
+        return await self.protocol.set_configuration(
+            self.seid,
+            int_seid,
+            configuration
+        )
+
+    async def open(self):
+        return await self.protocol.open(self.seid)
+
+    async def start(self):
+        return await self.protocol.start([self.seid])
+
+    async def stop(self):
+        return await self.protocol.suspend([self.seid])
+
+    async def close(self):
+        return await self.protocol.close(self.seid)
+
+    async def abort(self):
+        return await self.protocol.abort(self.seid)
+
+
+# -----------------------------------------------------------------------------
+class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
+    def __init__(self, protocol, seid, media_type, tsep, in_use, capabilities):
+        StreamEndPoint.__init__(self, seid, media_type, tsep, in_use, capabilities)
+        StreamEndPointProxy.__init__(self, protocol, seid)
+
+
+# -----------------------------------------------------------------------------
+class LocalStreamEndPoint(StreamEndPoint):
+    def __init__(self, protocol, seid, media_type, tsep, capabilities, configuration=[]):
+        super().__init__(seid, media_type, tsep, 0, capabilities)
+        self.protocol      = protocol
+        self.configuration = configuration
+        self.stream        = None
+
+    async def start(self):
+        pass
+
+    async def stop(self):
+        pass
+
+    async def close(self):
+        pass
+
+    def on_reconfigure_command(self, command):
+        pass
+
+    def on_get_configuration_command(self):
+        return Get_Configuration_Response(self.configuration)
+
+    def on_open_command(self):
+        pass
+
+    def on_start_command(self):
+        pass
+
+    def on_suspend_command(self):
+        pass
+
+    def on_close_command(self):
+        pass
+
+    def on_abort_command(self):
+        pass
+
+    def on_rtp_channel_open(self):
+        pass
+
+    def on_rtp_channel_close(self):
+        pass
+
+
+# -----------------------------------------------------------------------------
+class LocalSource(LocalStreamEndPoint, EventEmitter):
+    def __init__(self, protocol, seid, codec_capabilities, packet_pump):
+        capabilities = [
+            ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
+            codec_capabilities
+        ]
+        LocalStreamEndPoint.__init__(self, protocol, seid, codec_capabilities.media_type, AVDTP_TSEP_SRC, capabilities, capabilities)
+        EventEmitter.__init__(self)
+        self.packet_pump = packet_pump
+
+    async def start(self):
+        if self.packet_pump:
+            return await self.packet_pump.start(self.stream.rtp_channel)
+        else:
+            self.emit('start', self.stream.rtp_channel)
+
+    async def stop(self):
+        if self.packet_pump:
+            return await self.packet_pump.stop()
+        else:
+            self.emit('stop')
+
+    def on_set_configuration_command(self, configuration):
+        # For now, blindly accept the configuration
+        logger.debug(f'<<< received source configuration: {configuration}')
+        self.configuration = configuration
+
+    def on_start_command(self):
+        asyncio.get_running_loop().create_task(self.start())
+
+    def on_suspend_command(self):
+        asyncio.get_running_loop().create_task(self.stop())
+
+
+# -----------------------------------------------------------------------------
+class LocalSink(LocalStreamEndPoint, EventEmitter):
+    def __init__(self, protocol, seid, codec_capabilities):
+        capabilities = [
+            ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
+            codec_capabilities
+        ]
+        LocalStreamEndPoint.__init__(self, protocol, seid, codec_capabilities.media_type, AVDTP_TSEP_SNK, capabilities)
+        EventEmitter.__init__(self)
+
+    def on_set_configuration_command(self, configuration):
+        # For now, blindly accept the configuration
+        logger.debug(f'<<< received sink configuration: {configuration}')
+        self.configuration = configuration
+
+    def on_rtp_channel_open(self):
+        logger.debug(color('<<< RTP channel open', 'magenta'))
+        self.stream.rtp_channel.sink = self.on_avdtp_packet
+
+    def on_avdtp_packet(self, packet):
+        rtp_packet = MediaPacket.from_bytes(packet)
+        logger.debug(f'{color("<<< RTP Packet:", "green")} {rtp_packet} {rtp_packet.payload[:16].hex()}')
+        self.emit('rtp_packet', rtp_packet)
diff --git a/bumble/bridge.py b/bumble/bridge.py
new file mode 100644
index 0000000..2b4cd94
--- /dev/null
+++ b/bumble/bridge.py
@@ -0,0 +1,82 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+
+from .hci import HCI_Packet
+from .helpers import PacketTracer
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+class HCI_Bridge:
+    class Forwarder:
+        def __init__(self, hci_sink, sender_hci_sink, packet_filter, trace):
+            self.hci_sink        = hci_sink
+            self.sender_hci_sink = sender_hci_sink
+            self.packet_filter   = packet_filter
+            self.trace           = trace
+
+        def on_packet(self, packet):
+            # Convert the packet bytes to an object
+            hci_packet = HCI_Packet.from_bytes(packet)
+
+            # Filter the packet
+            if self.packet_filter is not None:
+                filtered = self.packet_filter(hci_packet)
+                if filtered is not None:
+                    packet, respond_to_sender = filtered
+                    hci_packet = HCI_Packet.from_bytes(packet)
+                    if respond_to_sender:
+                        self.sender_hci_sink.on_packet(packet)
+                        return
+
+            # Analyze the packet
+            self.trace(hci_packet)
+
+            # Bridge the packet
+            self.hci_sink.on_packet(packet)
+
+    def __init__(
+        self,
+        hci_host_source,
+        hci_host_sink,
+        hci_controller_source,
+        hci_controller_sink,
+        host_to_controller_filter = None,
+        controller_to_host_filter = None
+    ):
+        tracer = PacketTracer(emit_message=logger.info)
+        host_to_controller_forwarder = HCI_Bridge.Forwarder(
+            hci_controller_sink,
+            hci_host_sink,
+            host_to_controller_filter,
+            lambda packet: tracer.trace(packet, 0)
+        )
+        hci_host_source.set_packet_sink(host_to_controller_forwarder)
+
+        controller_to_host_forwarder = HCI_Bridge.Forwarder(
+            hci_host_sink,
+            hci_controller_sink,
+            controller_to_host_filter,
+            lambda packet: tracer.trace(packet, 1)
+        )
+        hci_controller_source.set_packet_sink(controller_to_host_forwarder)
diff --git a/bumble/company_ids.py b/bumble/company_ids.py
new file mode 100644
index 0000000..c9c9d1a
--- /dev/null
+++ b/bumble/company_ids.py
@@ -0,0 +1,2708 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+#  The contents of this file are generated by copy/pasting the output of
+#  the `generate_company_id_list.py` script
+# -----------------------------------------------------------------------------
+
+COMPANY_IDENTIFIERS = {
+    0x0000: "Ericsson Technology Licensing",
+    0x0001: "Nokia Mobile Phones",
+    0x0002: "Intel Corp.",
+    0x0003: "IBM Corp.",
+    0x0004: "Toshiba Corp.",
+    0x0005: "3Com",
+    0x0006: "Microsoft",
+    0x0007: "Lucent",
+    0x0008: "Motorola",
+    0x0009: "Infineon Technologies AG",
+    0x000A: "Qualcomm Technologies International, Ltd. (QTIL)",
+    0x000B: "Silicon Wave",
+    0x000C: "Digianswer A/S",
+    0x000D: "Texas Instruments Inc.",
+    0x000E: "Parthus Technologies Inc.",
+    0x000F: "Broadcom Corporation",
+    0x0010: "Mitel Semiconductor",
+    0x0011: "Widcomm, Inc.",
+    0x0012: "Zeevo, Inc.",
+    0x0013: "Atmel Corporation",
+    0x0014: "Mitsubishi Electric Corporation",
+    0x0015: "RTX Telecom A/S",
+    0x0016: "KC Technology Inc.",
+    0x0017: "Newlogic",
+    0x0018: "Transilica, Inc.",
+    0x0019: "Rohde & Schwarz GmbH & Co. KG",
+    0x001A: "TTPCom Limited",
+    0x001B: "Signia Technologies, Inc.",
+    0x001C: "Conexant Systems Inc.",
+    0x001D: "Qualcomm",
+    0x001E: "Inventel",
+    0x001F: "AVM Berlin",
+    0x0020: "BandSpeed, Inc.",
+    0x0021: "Mansella Ltd",
+    0x0022: "NEC Corporation",
+    0x0023: "WavePlus Technology Co., Ltd.",
+    0x0024: "Alcatel",
+    0x0025: "NXP Semiconductors (formerly Philips Semiconductors)",
+    0x0026: "C Technologies",
+    0x0027: "Open Interface",
+    0x0028: "R F Micro Devices",
+    0x0029: "Hitachi Ltd",
+    0x002A: "Symbol Technologies, Inc.",
+    0x002B: "Tenovis",
+    0x002C: "Macronix International Co. Ltd.",
+    0x002D: "GCT Semiconductor",
+    0x002E: "Norwood Systems",
+    0x002F: "MewTel Technology Inc.",
+    0x0030: "ST Microelectronics",
+    0x0031: "Synopsys, Inc.",
+    0x0032: "Red-M (Communications) Ltd",
+    0x0033: "Commil Ltd",
+    0x0034: "Computer Access Technology Corporation (CATC)",
+    0x0035: "Eclipse (HQ Espana) S.L.",
+    0x0036: "Renesas Electronics Corporation",
+    0x0037: "Mobilian Corporation",
+    0x0038: "Syntronix Corporation",
+    0x0039: "Integrated System Solution Corp.",
+    0x003A: "Panasonic Corporation (formerly Matsushita Electric Industrial Co., Ltd.)",
+    0x003B: "Gennum Corporation",
+    0x003C: "BlackBerry Limited  (formerly Research In Motion)",
+    0x003D: "IPextreme, Inc.",
+    0x003E: "Systems and Chips, Inc",
+    0x003F: "Bluetooth SIG, Inc",
+    0x0040: "Seiko Epson Corporation",
+    0x0041: "Integrated Silicon Solution Taiwan, Inc.",
+    0x0042: "CONWISE Technology Corporation Ltd",
+    0x0043: "PARROT AUTOMOTIVE SAS",
+    0x0044: "Socket Mobile",
+    0x0045: "Atheros Communications, Inc.",
+    0x0046: "MediaTek, Inc.",
+    0x0047: "Bluegiga",
+    0x0048: "Marvell Technology Group Ltd.",
+    0x0049: "3DSP Corporation",
+    0x004A: "Accel Semiconductor Ltd.",
+    0x004B: "Continental Automotive Systems",
+    0x004C: "Apple, Inc.",
+    0x004D: "Staccato Communications, Inc.",
+    0x004E: "Avago Technologies",
+    0x004F: "APT Ltd.",
+    0x0050: "SiRF Technology, Inc.",
+    0x0051: "Tzero Technologies, Inc.",
+    0x0052: "J&M Corporation",
+    0x0053: "Free2move AB",
+    0x0054: "3DiJoy Corporation",
+    0x0055: "Plantronics, Inc.",
+    0x0056: "Sony Ericsson Mobile Communications",
+    0x0057: "Harman International Industries, Inc.",
+    0x0058: "Vizio, Inc.",
+    0x0059: "Nordic Semiconductor ASA",
+    0x005A: "EM Microelectronic-Marin SA",
+    0x005B: "Ralink Technology Corporation",
+    0x005C: "Belkin International, Inc.",
+    0x005D: "Realtek Semiconductor Corporation",
+    0x005E: "Stonestreet One, LLC",
+    0x005F: "Wicentric, Inc.",
+    0x0060: "RivieraWaves S.A.S",
+    0x0061: "RDA Microelectronics",
+    0x0062: "Gibson Guitars",
+    0x0063: "MiCommand Inc.",
+    0x0064: "Band XI International, LLC",
+    0x0065: "Hewlett-Packard Company",
+    0x0066: "9Solutions Oy",
+    0x0067: "GN Netcom A/S",
+    0x0068: "General Motors",
+    0x0069: "A&D Engineering, Inc.",
+    0x006A: "MindTree Ltd.",
+    0x006B: "Polar Electro OY",
+    0x006C: "Beautiful Enterprise Co., Ltd.",
+    0x006D: "BriarTek, Inc",
+    0x006E: "Summit Data Communications, Inc.",
+    0x006F: "Sound ID",
+    0x0070: "Monster, LLC",
+    0x0071: "connectBlue AB",
+    0x0072: "ShangHai Super Smart Electronics Co. Ltd.",
+    0x0073: "Group Sense Ltd.",
+    0x0074: "Zomm, LLC",
+    0x0075: "Samsung Electronics Co. Ltd.",
+    0x0076: "Creative Technology Ltd.",
+    0x0077: "Laird Technologies",
+    0x0078: "Nike, Inc.",
+    0x0079: "lesswire AG",
+    0x007A: "MStar Semiconductor, Inc.",
+    0x007B: "Hanlynn Technologies",
+    0x007C: "A & R Cambridge",
+    0x007D: "Seers Technology Co., Ltd.",
+    0x007E: "Sports Tracking Technologies Ltd.",
+    0x007F: "Autonet Mobile",
+    0x0080: "DeLorme Publishing Company, Inc.",
+    0x0081: "WuXi Vimicro",
+    0x0082: "Sennheiser Communications A/S",
+    0x0083: "TimeKeeping Systems, Inc.",
+    0x0084: "Ludus Helsinki Ltd.",
+    0x0085: "BlueRadios, Inc.",
+    0x0086: "Equinux AG",
+    0x0087: "Garmin International, Inc.",
+    0x0088: "Ecotest",
+    0x0089: "GN ReSound A/S",
+    0x008A: "Jawbone",
+    0x008B: "Topcon Positioning Systems, LLC",
+    0x008C: "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)",
+    0x008D: "Zscan Software",
+    0x008E: "Quintic Corp",
+    0x008F: "Telit Wireless Solutions GmbH (formerly Stollmann E+V GmbH)",
+    0x0090: "Funai Electric Co., Ltd.",
+    0x0091: "Advanced PANMOBIL systems GmbH & Co. KG",
+    0x0092: "ThinkOptics, Inc.",
+    0x0093: "Universal Electronics, Inc.",
+    0x0094: "Airoha Technology Corp.",
+    0x0095: "NEC Lighting, Ltd.",
+    0x0096: "ODM Technology, Inc.",
+    0x0097: "ConnecteDevice Ltd.",
+    0x0098: "zero1.tv GmbH",
+    0x0099: "i.Tech Dynamic Global Distribution Ltd.",
+    0x009A: "Alpwise",
+    0x009B: "Jiangsu Toppower Automotive Electronics Co., Ltd.",
+    0x009C: "Colorfy, Inc.",
+    0x009D: "Geoforce Inc.",
+    0x009E: "Bose Corporation",
+    0x009F: "Suunto Oy",
+    0x00A0: "Kensington Computer Products Group",
+    0x00A1: "SR-Medizinelektronik",
+    0x00A2: "Vertu Corporation Limited",
+    0x00A3: "Meta Watch Ltd.",
+    0x00A4: "LINAK A/S",
+    0x00A5: "OTL Dynamics LLC",
+    0x00A6: "Panda Ocean Inc.",
+    0x00A7: "Visteon Corporation",
+    0x00A8: "ARP Devices Limited",
+    0x00A9: "MARELLI EUROPE S.P.A. (formerly Magneti Marelli S.p.A.)",
+    0x00AA: "CAEN RFID srl",
+    0x00AB: "Ingenieur-Systemgruppe Zahn GmbH",
+    0x00AC: "Green Throttle Games",
+    0x00AD: "Peter Systemtechnik GmbH",
+    0x00AE: "Omegawave Oy",
+    0x00AF: "Cinetix",
+    0x00B0: "Passif Semiconductor Corp",
+    0x00B1: "Saris Cycling Group, Inc",
+    0x00B2: "​Bekey A/S",
+    0x00B3: "​Clarinox Technologies Pty. Ltd.",
+    0x00B4: "​BDE Technology Co., Ltd.",
+    0x00B5: "Swirl Networks",
+    0x00B6: "​Meso international",
+    0x00B7: "​TreLab Ltd",
+    0x00B8: "​Qualcomm Innovation Center, Inc. (QuIC)",
+    0x00B9: "​​Johnson Controls, Inc.",
+    0x00BA: "​Starkey Laboratories Inc.",
+    0x00BB: "​​S-Power Electronics Limited",
+    0x00BC: "​​Ace Sensor Inc",
+    0x00BD: "​​Aplix Corporation",
+    0x00BE: "​​AAMP of America",
+    0x00BF: "​​Stalmart Technology Limited",
+    0x00C0: "​​AMICCOM Electronics Corporation",
+    0x00C1: "​​Shenzhen Excelsecu Data Technology Co.,Ltd",
+    0x00C2: "​​Geneq Inc.",
+    0x00C3: "​​adidas AG",
+    0x00C4: "​​LG Electronics​",
+    0x00C5: "​Onset Computer Corporation",
+    0x00C6: "​Selfly BV",
+    0x00C7: "​Quuppa Oy.",
+    0x00C8: "GeLo Inc",
+    0x00C9: "Evluma",
+    0x00CA: "MC10",
+    0x00CB: "Binauric SE",
+    0x00CC: "Beats Electronics",
+    0x00CD: "Microchip Technology Inc.",
+    0x00CE: "Elgato Systems GmbH",
+    0x00CF: "ARCHOS SA",
+    0x00D0: "Dexcom, Inc.",
+    0x00D1: "Polar Electro Europe B.V.",
+    0x00D2: "Dialog Semiconductor B.V.",
+    0x00D3: "Taixingbang Technology (HK) Co,. LTD.",
+    0x00D4: "Kawantech",
+    0x00D5: "Austco Communication Systems",
+    0x00D6: "Timex Group USA, Inc.",
+    0x00D7: "Qualcomm Technologies, Inc.",
+    0x00D8: "Qualcomm Connected Experiences, Inc.",
+    0x00D9: "Voyetra Turtle Beach",
+    0x00DA: "txtr GmbH",
+    0x00DB: "Biosentronics",
+    0x00DC: "Procter & Gamble",
+    0x00DD: "Hosiden Corporation",
+    0x00DE: "Muzik LLC",
+    0x00DF: "Misfit Wearables Corp",
+    0x00E0: "Google",
+    0x00E1: "Danlers Ltd",
+    0x00E2: "Semilink Inc",
+    0x00E3: "inMusic Brands, Inc",
+    0x00E4: "Laird Connectivity, Inc. formerly L.S. Research Inc.",
+    0x00E5: "Eden Software Consultants Ltd.",
+    0x00E6: "Freshtemp",
+    0x00E7: "​KS Technologies",
+    0x00E8: "​ACTS Technologies",
+    0x00E9: "​Vtrack Systems",
+    0x00EA: "​Nielsen-Kellerman Company",
+    0x00EB: "Server Technology Inc.",
+    0x00EC: "BioResearch Associates",
+    0x00ED: "Jolly Logic, LLC",
+    0x00EE: "Above Average Outcomes, Inc.",
+    0x00EF: "Bitsplitters GmbH",
+    0x00F0: "PayPal, Inc.",
+    0x00F1: "Witron Technology Limited",
+    0x00F2: "Morse Project Inc.",
+    0x00F3: "Kent Displays Inc.",
+    0x00F4: "Nautilus Inc.",
+    0x00F5: "Smartifier Oy",
+    0x00F6: "Elcometer Limited",
+    0x00F7: "VSN Technologies, Inc.",
+    0x00F8: "AceUni Corp., Ltd.",
+    0x00F9: "StickNFind",
+    0x00FA: "Crystal Code AB",
+    0x00FB: "KOUKAAM a.s.",
+    0x00FC: "Delphi Corporation",
+    0x00FD: "ValenceTech Limited",
+    0x00FE: "Stanley Black and Decker",
+    0x00FF: "Typo Products, LLC",
+    0x0100: "TomTom International BV",
+    0x0101: "Fugoo, Inc.",
+    0x0102: "Keiser Corporation",
+    0x0103: "Bang & Olufsen A/S",
+    0x0104: "PLUS Location Systems Pty Ltd",
+    0x0105: "Ubiquitous Computing Technology Corporation",
+    0x0106: "Innovative Yachtter Solutions",
+    0x0107: "William Demant Holding A/S",
+    0x0108: "Chicony Electronics Co., Ltd.",
+    0x0109: "Atus BV",
+    0x010A: "Codegate Ltd",
+    0x010B: "ERi, Inc",
+    0x010C: "Transducers Direct, LLC",
+    0x010D: "DENSO TEN LIMITED (formerly Fujitsu Ten LImited)",
+    0x010E: "Audi AG",
+    0x010F: "HiSilicon Technologies CO., LIMITED",
+    0x0110: "Nippon Seiki Co., Ltd.",
+    0x0111: "Steelseries ApS",
+    0x0112: "Visybl Inc.",
+    0x0113: "Openbrain Technologies, Co., Ltd.",
+    0x0114: "Xensr",
+    0x0115: "e.solutions",
+    0x0116: "10AK Technologies",
+    0x0117: "Wimoto Technologies Inc",
+    0x0118: "Radius Networks, Inc.",
+    0x0119: "Wize Technology Co., Ltd.",
+    0x011A: "Qualcomm Labs, Inc.",
+    0x011B: "Hewlett Packard Enterprise",
+    0x011C: "Baidu",
+    0x011D: "Arendi AG",
+    0x011E: "Skoda Auto a.s.",
+    0x011F: "Volkswagen AG",
+    0x0120: "Porsche AG",
+    0x0121: "Sino Wealth Electronic Ltd.",
+    0x0122: "AirTurn, Inc.",
+    0x0123: "Kinsa, Inc",
+    0x0124: "HID Global",
+    0x0125: "SEAT es",
+    0x0126: "Promethean Ltd.",
+    0x0127: "Salutica Allied Solutions",
+    0x0128: "GPSI Group Pty Ltd",
+    0x0129: "Nimble Devices Oy",
+    0x012A: "Changzhou Yongse Infotech  Co., Ltd.",
+    0x012B: "SportIQ",
+    0x012C: "TEMEC Instruments B.V.",
+    0x012D: "Sony Corporation",
+    0x012E: "ASSA ABLOY",
+    0x012F: "Clarion Co. Inc.",
+    0x0130: "Warehouse Innovations",
+    0x0131: "Cypress Semiconductor",
+    0x0132: "MADS Inc",
+    0x0133: "Blue Maestro Limited",
+    0x0134: "Resolution Products, Ltd.",
+    0x0135: "Aireware LLC",
+    0x0136: "Silvair, Inc.",
+    0x0137: "Prestigio Plaza Ltd.",
+    0x0138: "NTEO Inc.",
+    0x0139: "Focus Systems Corporation",
+    0x013A: "Tencent Holdings Ltd.",
+    0x013B: "Allegion",
+    0x013C: "Murata Manufacturing Co., Ltd.",
+    0x013D: "WirelessWERX",
+    0x013E: "Nod, Inc.",
+    0x013F: "B&B Manufacturing Company",
+    0x0140: "Alpine Electronics (China) Co., Ltd",
+    0x0141: "FedEx Services",
+    0x0142: "Grape Systems Inc.",
+    0x0143: "Bkon Connect",
+    0x0144: "Lintech GmbH",
+    0x0145: "Novatel Wireless",
+    0x0146: "Ciright",
+    0x0147: "Mighty Cast, Inc.",
+    0x0148: "Ambimat Electronics",
+    0x0149: "Perytons Ltd.",
+    0x014A: "Tivoli Audio, LLC",
+    0x014B: "Master Lock",
+    0x014C: "Mesh-Net Ltd",
+    0x014D: "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD.",
+    0x014E: "Tangerine, Inc.",
+    0x014F: "B&W Group Ltd.",
+    0x0150: "Pioneer Corporation",
+    0x0151: "OnBeep",
+    0x0152: "Vernier Software & Technology",
+    0x0153: "ROL Ergo",
+    0x0154: "Pebble Technology",
+    0x0155: "NETATMO",
+    0x0156: "Accumulate AB",
+    0x0157: "Anhui Huami Information Technology Co., Ltd.",
+    0x0158: "Inmite s.r.o.",
+    0x0159: "ChefSteps, Inc.",
+    0x015A: "micas AG",
+    0x015B: "Biomedical Research Ltd.",
+    0x015C: "Pitius Tec S.L.",
+    0x015D: "Estimote, Inc.",
+    0x015E: "Unikey Technologies, Inc.",
+    0x015F: "Timer Cap Co.",
+    0x0160: "Awox formerly AwoX",
+    0x0161: "yikes",
+    0x0162: "MADSGlobalNZ Ltd.",
+    0x0163: "PCH International",
+    0x0164: "Qingdao Yeelink Information Technology Co., Ltd.",
+    0x0165: "Milwaukee Tool (Formally Milwaukee Electric Tools)",
+    0x0166: "MISHIK Pte Ltd",
+    0x0167: "Ascensia Diabetes Care US Inc.",
+    0x0168: "Spicebox LLC",
+    0x0169: "emberlight",
+    0x016A: "Cooper-Atkins Corporation",
+    0x016B: "Qblinks",
+    0x016C: "MYSPHERA",
+    0x016D: "LifeScan Inc",
+    0x016E: "Volantic AB",
+    0x016F: "Podo Labs, Inc",
+    0x0170: "Roche Diabetes Care AG",
+    0x0171: "Amazon.com Services, LLC (formerly Amazon Fulfillment Service)",
+    0x0172: "Connovate Technology Private Limited",
+    0x0173: "Kocomojo, LLC",
+    0x0174: "Everykey Inc.",
+    0x0175: "Dynamic Controls",
+    0x0176: "SentriLock",
+    0x0177: "I-SYST inc.",
+    0x0178: "CASIO COMPUTER CO., LTD.",
+    0x0179: "LAPIS Technology Co., Ltd. formerly LAPIS Semiconductor Co., Ltd.",
+    0x017A: "Telemonitor, Inc.",
+    0x017B: "taskit GmbH",
+    0x017C: "Daimler AG",
+    0x017D: "BatAndCat",
+    0x017E: "BluDotz Ltd",
+    0x017F: "XTel Wireless ApS",
+    0x0180: "Gigaset Communications GmbH",
+    0x0181: "Gecko Health Innovations, Inc.",
+    0x0182: "HOP Ubiquitous",
+    0x0183: "Walt Disney",
+    0x0184: "Nectar",
+    0x0185: "bel'apps LLC",
+    0x0186: "CORE Lighting Ltd",
+    0x0187: "Seraphim Sense Ltd",
+    0x0188: "Unico RBC",
+    0x0189: "Physical Enterprises Inc.",
+    0x018A: "Able Trend Technology Limited",
+    0x018B: "Konica Minolta, Inc.",
+    0x018C: "Wilo SE",
+    0x018D: "Extron Design Services",
+    0x018E: "Fitbit, Inc.",
+    0x018F: "Fireflies Systems",
+    0x0190: "Intelletto Technologies Inc.",
+    0x0191: "FDK CORPORATION",
+    0x0192: "Cloudleaf, Inc",
+    0x0193: "Maveric Automation LLC",
+    0x0194: "Acoustic Stream Corporation",
+    0x0195: "Zuli",
+    0x0196: "Paxton Access Ltd",
+    0x0197: "WiSilica Inc.",
+    0x0198: "VENGIT Korlatolt Felelossegu Tarsasag",
+    0x0199: "SALTO SYSTEMS S.L.",
+    0x019A: "TRON Forum (formerly T-Engine Forum)",
+    0x019B: "CUBETECH s.r.o.",
+    0x019C: "Cokiya Incorporated",
+    0x019D: "CVS Health",
+    0x019E: "Ceruus",
+    0x019F: "Strainstall Ltd",
+    0x01A0: "Channel Enterprises (HK) Ltd.",
+    0x01A1: "FIAMM",
+    0x01A2: "GIGALANE.CO.,LTD",
+    0x01A3: "EROAD",
+    0x01A4: "Mine Safety Appliances",
+    0x01A5: "Icon Health and Fitness",
+    0x01A6: "Wille Engineering (formely as Asandoo GmbH)",
+    0x01A7: "ENERGOUS CORPORATION",
+    0x01A8: "Taobao",
+    0x01A9: "Canon Inc.",
+    0x01AA: "Geophysical Technology Inc.",
+    0x01AB: "Facebook, Inc.",
+    0x01AC: "Trividia Health, Inc.",
+    0x01AD: "FlightSafety International",
+    0x01AE: "Earlens Corporation",
+    0x01AF: "Sunrise Micro Devices, Inc.",
+    0x01B0: "Star Micronics Co., Ltd.",
+    0x01B1: "Netizens Sp. z o.o.",
+    0x01B2: "Nymi Inc.",
+    0x01B3: "Nytec, Inc.",
+    0x01B4: "Trineo Sp. z o.o.",
+    0x01B5: "Nest Labs Inc.",
+    0x01B6: "LM Technologies Ltd",
+    0x01B7: "General Electric Company",
+    0x01B8: "i+D3 S.L.",
+    0x01B9: "HANA Micron",
+    0x01BA: "Stages Cycling LLC",
+    0x01BB: "Cochlear Bone Anchored Solutions AB",
+    0x01BC: "SenionLab AB",
+    0x01BD: "Syszone Co., Ltd",
+    0x01BE: "Pulsate Mobile Ltd.",
+    0x01BF: "Hong Kong HunterSun Electronic Limited",
+    0x01C0: "pironex GmbH",
+    0x01C1: "BRADATECH Corp.",
+    0x01C2: "Transenergooil AG",
+    0x01C3: "Bunch",
+    0x01C4: "DME Microelectronics",
+    0x01C5: "Bitcraze AB",
+    0x01C6: "HASWARE Inc.",
+    0x01C7: "Abiogenix Inc.",
+    0x01C8: "Poly-Control ApS",
+    0x01C9: "Avi-on",
+    0x01CA: "Laerdal Medical AS",
+    0x01CB: "Fetch My Pet",
+    0x01CC: "Sam Labs Ltd.",
+    0x01CD: "Chengdu Synwing Technology Ltd",
+    0x01CE: "HOUWA SYSTEM DESIGN, k.k.",
+    0x01CF: "BSH",
+    0x01D0: "Primus Inter Pares Ltd",
+    0x01D1: "August Home, Inc",
+    0x01D2: "Gill Electronics",
+    0x01D3: "Sky Wave Design",
+    0x01D4: "Newlab S.r.l.",
+    0x01D5: "ELAD srl",
+    0x01D6: "G-wearables inc.",
+    0x01D7: "Squadrone Systems Inc.",
+    0x01D8: "Code Corporation",
+    0x01D9: "Savant Systems LLC",
+    0x01DA: "Logitech International SA",
+    0x01DB: "Innblue Consulting",
+    0x01DC: "iParking Ltd.",
+    0x01DD: "Koninklijke Philips Electronics N.V.",
+    0x01DE: "Minelab Electronics Pty Limited",
+    0x01DF: "Bison Group Ltd.",
+    0x01E0: "Widex A/S",
+    0x01E1: "Jolla Ltd",
+    0x01E2: "Lectronix, Inc.",
+    0x01E3: "Caterpillar Inc",
+    0x01E4: "Freedom Innovations",
+    0x01E5: "Dynamic Devices Ltd",
+    0x01E6: "Technology Solutions (UK) Ltd",
+    0x01E7: "IPS Group Inc.",
+    0x01E8: "STIR",
+    0x01E9: "Sano, Inc.",
+    0x01EA: "Advanced Application Design, Inc.",
+    0x01EB: "AutoMap LLC",
+    0x01EC: "Spreadtrum Communications Shanghai Ltd",
+    0x01ED: "CuteCircuit LTD",
+    0x01EE: "Valeo Service",
+    0x01EF: "Fullpower Technologies, Inc.",
+    0x01F0: "KloudNation",
+    0x01F1: "Zebra Technologies Corporation",
+    0x01F2: "Itron, Inc.",
+    0x01F3: "The University of Tokyo",
+    0x01F4: "UTC Fire and Security",
+    0x01F5: "Cool Webthings Limited",
+    0x01F6: "DJO Global",
+    0x01F7: "Gelliner Limited",
+    0x01F8: "Anyka (Guangzhou) Microelectronics Technology Co, LTD",
+    0x01F9: "Medtronic Inc.",
+    0x01FA: "Gozio Inc.",
+    0x01FB: "Form Lifting, LLC",
+    0x01FC: "Wahoo Fitness, LLC",
+    0x01FD: "Kontakt Micro-Location Sp. z o.o.",
+    0x01FE: "Radio Systems Corporation",
+    0x01FF: "Freescale Semiconductor, Inc.",
+    0x0200: "Verifone Systems Pte Ltd. Taiwan Branch",
+    0x0201: "AR Timing",
+    0x0202: "Rigado LLC",
+    0x0203: "Kemppi Oy",
+    0x0204: "Tapcentive Inc.",
+    0x0205: "Smartbotics Inc.",
+    0x0206: "Otter Products, LLC",
+    0x0207: "STEMP Inc.",
+    0x0208: "LumiGeek LLC",
+    0x0209: "InvisionHeart Inc.",
+    0x020A: "Macnica Inc.",
+    0x020B: "Jaguar Land Rover Limited",
+    0x020C: "CoroWare Technologies, Inc",
+    0x020D: "Simplo Technology Co., LTD",
+    0x020E: "Omron Healthcare Co., LTD",
+    0x020F: "Comodule GMBH",
+    0x0210: "ikeGPS",
+    0x0211: "Telink Semiconductor Co. Ltd",
+    0x0212: "Interplan Co., Ltd",
+    0x0213: "Wyler AG",
+    0x0214: "IK Multimedia Production srl",
+    0x0215: "Lukoton Experience Oy",
+    0x0216: "MTI Ltd",
+    0x0217: "Tech4home, Lda",
+    0x0218: "Hiotech AB",
+    0x0219: "DOTT Limited",
+    0x021A: "Blue Speck Labs, LLC",
+    0x021B: "Cisco Systems, Inc",
+    0x021C: "Mobicomm Inc",
+    0x021D: "Edamic",
+    0x021E: "Goodnet, Ltd",
+    0x021F: "Luster Leaf Products  Inc",
+    0x0220: "Manus Machina BV",
+    0x0221: "Mobiquity Networks Inc",
+    0x0222: "Praxis Dynamics",
+    0x0223: "Philip Morris Products S.A.",
+    0x0224: "Comarch SA",
+    0x0225: "Nestlé Nespresso S.A.",
+    0x0226: "Merlinia A/S",
+    0x0227: "LifeBEAM Technologies",
+    0x0228: "Twocanoes Labs, LLC",
+    0x0229: "Muoverti Limited",
+    0x022A: "Stamer Musikanlagen GMBH",
+    0x022B: "Tesla Motors",
+    0x022C: "Pharynks Corporation",
+    0x022D: "Lupine",
+    0x022E: "Siemens AG",
+    0x022F: "Huami (Shanghai) Culture Communication CO., LTD",
+    0x0230: "Foster Electric Company, Ltd",
+    0x0231: "ETA SA",
+    0x0232: "x-Senso Solutions Kft",
+    0x0233: "Shenzhen SuLong Communication Ltd",
+    0x0234: "FengFan (BeiJing) Technology Co, Ltd",
+    0x0235: "Qrio Inc",
+    0x0236: "Pitpatpet Ltd",
+    0x0237: "MSHeli s.r.l.",
+    0x0238: "Trakm8 Ltd",
+    0x0239: "JIN CO, Ltd",
+    0x023A: "Alatech Tehnology",
+    0x023B: "Beijing CarePulse Electronic Technology Co, Ltd",
+    0x023C: "Awarepoint",
+    0x023D: "ViCentra B.V.",
+    0x023E: "Raven Industries",
+    0x023F: "WaveWare Technologies Inc.",
+    0x0240: "Argenox Technologies",
+    0x0241: "Bragi GmbH",
+    0x0242: "16Lab Inc",
+    0x0243: "Masimo Corp",
+    0x0244: "Iotera Inc",
+    0x0245: "Endress+Hauser ",
+    0x0246: "ACKme Networks, Inc.",
+    0x0247: "FiftyThree Inc.",
+    0x0248: "Parker Hannifin Corp",
+    0x0249: "Transcranial Ltd",
+    0x024A: "Uwatec AG",
+    0x024B: "Orlan LLC",
+    0x024C: "Blue Clover Devices",
+    0x024D: "M-Way Solutions GmbH",
+    0x024E: "Microtronics Engineering GmbH",
+    0x024F: "Schneider Schreibgeräte GmbH",
+    0x0250: "Sapphire Circuits LLC",
+    0x0251: "Lumo Bodytech Inc.",
+    0x0252: "UKC Technosolution",
+    0x0253: "Xicato Inc.",
+    0x0254: "Playbrush",
+    0x0255: "Dai Nippon Printing Co., Ltd.",
+    0x0256: "G24 Power Limited",
+    0x0257: "AdBabble Local Commerce Inc.",
+    0x0258: "Devialet SA",
+    0x0259: "ALTYOR",
+    0x025A: "University of Applied Sciences Valais/Haute Ecole Valaisanne",
+    0x025B: "Five Interactive, LLC dba Zendo",
+    0x025C: "NetEaseīŧˆHangzhouīŧ‰Network co.Ltd.",
+    0x025D: "Lexmark International Inc.",
+    0x025E: "Fluke Corporation",
+    0x025F: "Yardarm Technologies",
+    0x0260: "SensaRx",
+    0x0261: "SECVRE GmbH",
+    0x0262: "Glacial Ridge Technologies",
+    0x0263: "Identiv, Inc.",
+    0x0264: "DDS, Inc.",
+    0x0265: "SMK Corporation",
+    0x0266: "Schawbel Technologies LLC",
+    0x0267: "XMI Systems SA",
+    0x0268: "Cerevo",
+    0x0269: "Torrox GmbH & Co KG",
+    0x026A: "Gemalto",
+    0x026B: "DEKA Research & Development Corp.",
+    0x026C: "Domster Tadeusz Szydlowski",
+    0x026D: "Technogym SPA",
+    0x026E: "FLEURBAEY BVBA",
+    0x026F: "Aptcode Solutions",
+    0x0270: "LSI ADL Technology",
+    0x0271: "Animas Corp",
+    0x0272: "Alps Alpine Co., Ltd.",
+    0x0273: "OCEASOFT",
+    0x0274: "Motsai Research",
+    0x0275: "Geotab",
+    0x0276: "E.G.O. Elektro-Geraetebau GmbH",
+    0x0277: "bewhere inc",
+    0x0278: "Johnson Outdoors Inc",
+    0x0279: "steute Schaltgerate GmbH & Co. KG",
+    0x027A: "Ekomini inc.",
+    0x027B: "DEFA AS",
+    0x027C: "Aseptika Ltd",
+    0x027D: "HUAWEI Technologies Co., Ltd.",
+    0x027E: "HabitAware, LLC",
+    0x027F: "ruwido austria gmbh",
+    0x0280: "ITEC corporation",
+    0x0281: "StoneL",
+    0x0282: "Sonova AG",
+    0x0283: "Maven Machines, Inc.",
+    0x0284: "Synapse Electronics",
+    0x0285: "Standard Innovation Inc.",
+    0x0286: "RF Code, Inc.",
+    0x0287: "Wally Ventures S.L.",
+    0x0288: "Willowbank Electronics Ltd",
+    0x0289: "SK Telecom",
+    0x028A: "Jetro AS",
+    0x028B: "Code Gears LTD",
+    0x028C: "NANOLINK APS",
+    0x028D: "IF, LLC",
+    0x028E: "RF Digital Corp",
+    0x028F: "Church & Dwight Co., Inc",
+    0x0290: "Multibit Oy",
+    0x0291: "CliniCloud Inc",
+    0x0292: "SwiftSensors",
+    0x0293: "Blue Bite",
+    0x0294: "ELIAS GmbH",
+    0x0295: "Sivantos GmbH",
+    0x0296: "Petzl",
+    0x0297: "storm power ltd",
+    0x0298: "EISST Ltd",
+    0x0299: "Inexess Technology Simma KG",
+    0x029A: "Currant, Inc.",
+    0x029B: "C2 Development, Inc.",
+    0x029C: "Blue Sky Scientific, LLC",
+    0x029D: "ALOTTAZS LABS, LLC",
+    0x029E: "Kupson spol. s r.o.",
+    0x029F: "Areus Engineering GmbH",
+    0x02A0: "Impossible Camera GmbH",
+    0x02A1: "InventureTrack Systems",
+    0x02A2: "LockedUp",
+    0x02A3: "Itude",
+    0x02A4: "Pacific Lock Company",
+    0x02A5: "Tendyron Corporation ( å¤Šåœ°čžį§‘æŠ€č‚ĄäģŊ有限å…Ŧ司 )",
+    0x02A6: "Robert Bosch GmbH",
+    0x02A7: "Illuxtron international B.V.",
+    0x02A8: "miSport Ltd.",
+    0x02A9: "Chargelib",
+    0x02AA: "Doppler Lab",
+    0x02AB: "BBPOS Limited",
+    0x02AC: "RTB Elektronik GmbH & Co. KG",
+    0x02AD: "Rx Networks, Inc.",
+    0x02AE: "WeatherFlow, Inc.",
+    0x02AF: "Technicolor USA Inc.",
+    0x02B0: "Bestechnic(Shanghai),Ltd",
+    0x02B1: "Raden Inc",
+    0x02B2: "JouZen Oy",
+    0x02B3: "CLABER S.P.A.",
+    0x02B4: "Hyginex, Inc.",
+    0x02B5: "HANSHIN ELECTRIC RAILWAY CO.,LTD.",
+    0x02B6: "Schneider Electric",
+    0x02B7: "Oort Technologies LLC",
+    0x02B8: "Chrono Therapeutics",
+    0x02B9: "Rinnai Corporation",
+    0x02BA: "Swissprime Technologies AG",
+    0x02BB: "Koha.,Co.Ltd",
+    0x02BC: "Genevac Ltd",
+    0x02BD: "Chemtronics",
+    0x02BE: "Seguro Technology Sp. z o.o.",
+    0x02BF: "Redbird Flight Simulations",
+    0x02C0: "Dash Robotics",
+    0x02C1: "LINE Corporation",
+    0x02C2: "Guillemot Corporation",
+    0x02C3: "Techtronic Power Tools Technology Limited",
+    0x02C4: "Wilson Sporting Goods",
+    0x02C5: "Lenovo (Singapore) Pte Ltd. ( 联æƒŗīŧˆæ–°åŠ åĄīŧ‰ )",
+    0x02C6: "Ayatan Sensors",
+    0x02C7: "Electronics Tomorrow Limited",
+    0x02C8: "VASCO Data Security International, Inc.",
+    0x02C9: "PayRange Inc.",
+    0x02CA: "ABOV Semiconductor",
+    0x02CB: "AINA-Wireless Inc.",
+    0x02CC: "Eijkelkamp Soil & Water",
+    0x02CD: "BMA ergonomics b.v.",
+    0x02CE: "Teva Branded Pharmaceutical Products R&D, Inc.",
+    0x02CF: "Anima",
+    0x02D0: "3M",
+    0x02D1: "Empatica Srl",
+    0x02D2: "Afero, Inc.",
+    0x02D3: "Powercast Corporation",
+    0x02D4: "Secuyou ApS",
+    0x02D5: "OMRON Corporation",
+    0x02D6: "Send Solutions",
+    0x02D7: "NIPPON SYSTEMWARE CO.,LTD.",
+    0x02D8: "Neosfar",
+    0x02D9: "Fliegl Agrartechnik GmbH",
+    0x02DA: "Gilvader",
+    0x02DB: "Digi International Inc (R)",
+    0x02DC: "DeWalch Technologies, Inc.",
+    0x02DD: "Flint Rehabilitation Devices, LLC",
+    0x02DE: "Samsung SDS Co., Ltd.",
+    0x02DF: "Blur Product Development",
+    0x02E0: "University of Michigan",
+    0x02E1: "Victron Energy BV",
+    0x02E2: "NTT docomo",
+    0x02E3: "Carmanah Technologies Corp.",
+    0x02E4: "Bytestorm Ltd.",
+    0x02E5: "Espressif Incorporated ( 䚐é‘ĢäŋĄæ¯į§‘技(上æĩˇ)有限å…Ŧ司 )",
+    0x02E6: "Unwire",
+    0x02E7: "Connected Yard, Inc.",
+    0x02E8: "American Music Environments",
+    0x02E9: "Sensogram Technologies, Inc.",
+    0x02EA: "Fujitsu Limited",
+    0x02EB: "Ardic Technology",
+    0x02EC: "Delta Systems, Inc",
+    0x02ED: "HTC Corporation ",
+    0x02EE: "Citizen Holdings Co., Ltd. ",
+    0x02EF: "SMART-INNOVATION.inc",
+    0x02F0: "Blackrat Software ",
+    0x02F1: "The Idea Cave, LLC",
+    0x02F2: "GoPro, Inc.",
+    0x02F3: "AuthAir, Inc",
+    0x02F4: "Vensi, Inc.",
+    0x02F5: "Indagem Tech LLC",
+    0x02F6: "Intemo Technologies",
+    0x02F7: "DreamVisions co., Ltd.",
+    0x02F8: "Runteq Oy Ltd",
+    0x02F9: "IMAGINATION TECHNOLOGIES LTD ",
+    0x02FA: "CoSTAR TEchnologies",
+    0x02FB: "Clarius Mobile Health Corp.",
+    0x02FC: "Shanghai Frequen Microelectronics Co., Ltd.",
+    0x02FD: "Uwanna, Inc.",
+    0x02FE: "Lierda Science & Technology Group Co., Ltd.",
+    0x02FF: "Silicon Laboratories",
+    0x0300: "World Moto Inc.",
+    0x0301: "Giatec Scientific Inc.",
+    0x0302: "Loop Devices, Inc",
+    0x0303: "IACA electronique",
+    0x0304: "Proxy Technologies, Inc.",
+    0x0305: "Swipp ApS",
+    0x0306: "Life Laboratory Inc. ",
+    0x0307: "FUJI INDUSTRIAL CO.,LTD.",
+    0x0308: "Surefire, LLC",
+    0x0309: "Dolby Labs",
+    0x030A: "Ellisys",
+    0x030B: "Magnitude Lighting Converters",
+    0x030C: "Hilti AG",
+    0x030D: "Devdata S.r.l.",
+    0x030E: "Deviceworx",
+    0x030F: "Shortcut Labs",
+    0x0310: "SGL Italia S.r.l.",
+    0x0311: "PEEQ DATA",
+    0x0312: "Ducere Technologies Pvt Ltd ",
+    0x0313: "DiveNav, Inc. ",
+    0x0314: "RIIG AI Sp. z o.o.",
+    0x0315: "Thermo Fisher Scientific ",
+    0x0316: "AG Measurematics Pvt. Ltd. ",
+    0x0317: "CHUO Electronics CO., LTD. ",
+    0x0318: "Aspenta International ",
+    0x0319: "Eugster Frismag AG ",
+    0x031A: "Amber wireless GmbH ",
+    0x031B: "HQ Inc ",
+    0x031C: "Lab Sensor Solutions ",
+    0x031D: "Enterlab ApS ",
+    0x031E: "Eyefi, Inc.",
+    0x031F: "MetaSystem S.p.A. ",
+    0x0320: "SONO ELECTRONICS. CO., LTD ",
+    0x0321: "Jewelbots ",
+    0x0322: "Compumedics Limited ",
+    0x0323: "Rotor Bike Components ",
+    0x0324: "Astro, Inc. ",
+    0x0325: "Amotus Solutions ",
+    0x0326: "Healthwear Technologies (Changzhou)Ltd ",
+    0x0327: "Essex Electronics ",
+    0x0328: "Grundfos A/S",
+    0x0329: "Eargo, Inc. ",
+    0x032A: "Electronic Design Lab ",
+    0x032B: "ESYLUX ",
+    0x032C: "NIPPON SMT.CO.,Ltd",
+    0x032D: "BM innovations GmbH ",
+    0x032E: "indoormap",
+    0x032F: "OttoQ Inc ",
+    0x0330: "North Pole Engineering ",
+    0x0331: "3flares Technologies Inc.",
+    0x0332: "Electrocompaniet A.S. ",
+    0x0333: "Mul-T-Lock",
+    0x0334: "Corentium AS ",
+    0x0335: "Enlighted Inc",
+    0x0336: "GISTIC",
+    0x0337: "AJP2 Holdings, LLC",
+    0x0338: "COBI GmbH ",
+    0x0339: "Blue Sky Scientific, LLC ",
+    0x033A: "Appception, Inc.",
+    0x033B: "Courtney Thorne Limited ",
+    0x033C: "Virtuosys",
+    0x033D: "TPV Technology Limited ",
+    0x033E: "Monitra SA",
+    0x033F: "Automation Components, Inc. ",
+    0x0340: "Letsense s.r.l. ",
+    0x0341: "Etesian Technologies LLC ",
+    0x0342: "GERTEC BRASIL LTDA. ",
+    0x0343: "Drekker Development Pty. Ltd.",
+    0x0344: "Whirl Inc ",
+    0x0345: "Locus Positioning ",
+    0x0346: "Acuity Brands Lighting, Inc ",
+    0x0347: "Prevent Biometrics ",
+    0x0348: "Arioneo",
+    0x0349: "VersaMe ",
+    0x034A: "Vaddio ",
+    0x034B: "Libratone A/S ",
+    0x034C: "HM Electronics, Inc. ",
+    0x034D: "TASER International, Inc.",
+    0x034E: "SafeTrust Inc. ",
+    0x034F: "Heartland Payment Systems ",
+    0x0350: "Bitstrata Systems Inc. ",
+    0x0351: "Pieps GmbH ",
+    0x0352: "iRiding(Xiamen)Technology Co.,Ltd.",
+    0x0353: "Alpha Audiotronics, Inc. ",
+    0x0354: "TOPPAN FORMS CO.,LTD. ",
+    0x0355: "Sigma Designs, Inc. ",
+    0x0356: "Spectrum Brands, Inc. ",
+    0x0357: "Polymap Wireless ",
+    0x0358: "MagniWare Ltd.",
+    0x0359: "Novotec Medical GmbH ",
+    0x035A: "Medicom Innovation Partner a/s ",
+    0x035B: "Matrix Inc. ",
+    0x035C: "Eaton Corporation ",
+    0x035D: "KYS",
+    0x035E: "Naya Health, Inc. ",
+    0x035F: "Acromag ",
+    0x0360: "Insulet Corporation ",
+    0x0361: "Wellinks Inc. ",
+    0x0362: "ON Semiconductor",
+    0x0363: "FREELAP SA ",
+    0x0364: "Favero Electronics Srl ",
+    0x0365: "BioMech Sensor LLC ",
+    0x0366: "BOLTT Sports technologies Private limited",
+    0x0367: "Saphe International ",
+    0x0368: "Metormote AB ",
+    0x0369: "littleBits ",
+    0x036A: "SetPoint Medical ",
+    0x036B: "BRControls Products BV ",
+    0x036C: "Zipcar ",
+    0x036D: "AirBolt Pty Ltd ",
+    0x036E: "KeepTruckin Inc ",
+    0x036F: "Motiv, Inc. ",
+    0x0370: "Wazombi Labs OÜ ",
+    0x0371: "ORBCOMM",
+    0x0372: "Nixie Labs, Inc.",
+    0x0373: "AppNearMe Ltd",
+    0x0374: "Holman Industries",
+    0x0375: "Expain AS",
+    0x0376: "Electronic Temperature Instruments Ltd",
+    0x0377: "Plejd AB",
+    0x0378: "Propeller Health",
+    0x0379: "Shenzhen iMCO Electronic Technology Co.,Ltd",
+    0x037A: "Algoria",
+    0x037B: "Apption Labs Inc.",
+    0x037C: "Cronologics Corporation",
+    0x037D: "MICRODIA Ltd.",
+    0x037E: "lulabytes S.L.",
+    0x037F: "Société des Produits Nestlé S.A. (formerly Nestec S.A.)",
+    0x0380: "LLC \"MEGA-F service\"",
+    0x0381: "Sharp Corporation",
+    0x0382: "Precision Outcomes Ltd",
+    0x0383: "Kronos Incorporated",
+    0x0384: "OCOSMOS Co., Ltd.",
+    0x0385: "Embedded Electronic Solutions Ltd. dba e2Solutions",
+    0x0386: "Aterica Inc.",
+    0x0387: "BluStor PMC, Inc.",
+    0x0388: "Kapsch TrafficCom AB",
+    0x0389: "ActiveBlu Corporation",
+    0x038A: "Kohler Mira Limited",
+    0x038B: "Noke",
+    0x038C: "Appion Inc.",
+    0x038D: "Resmed Ltd",
+    0x038E: "Crownstone B.V.",
+    0x038F: "Xiaomi Inc.",
+    0x0390: "INFOTECH s.r.o.",
+    0x0391: "Thingsquare AB",
+    0x0392: "T&D",
+    0x0393: "LAVAZZA S.p.A.",
+    0x0394: "Netclearance Systems, Inc.",
+    0x0395: "SDATAWAY",
+    0x0396: "BLOKS GmbH",
+    0x0397: "LEGO System A/S",
+    0x0398: "Thetatronics Ltd",
+    0x0399: "Nikon Corporation",
+    0x039A: "NeST",
+    0x039B: "South Silicon Valley Microelectronics",
+    0x039C: "ALE International",
+    0x039D: "CareView Communications, Inc.",
+    0x039E: "SchoolBoard Limited",
+    0x039F: "Molex Corporation",
+    0x03A0: "IVT Wireless Limited",
+    0x03A1: "Alpine Labs LLC",
+    0x03A2: "Candura Instruments",
+    0x03A3: "SmartMovt Technology Co., Ltd",
+    0x03A4: "Token Zero Ltd",
+    0x03A5: "ACE CAD Enterprise Co., Ltd. (ACECAD)",
+    0x03A6: "Medela, Inc",
+    0x03A7: "AeroScout",
+    0x03A8: "Esrille Inc.",
+    0x03A9: "THINKERLY SRL",
+    0x03AA: "Exon Sp. z o.o.",
+    0x03AB: "Meizu Technology Co., Ltd.",
+    0x03AC: "Smablo LTD",
+    0x03AD: "XiQ",
+    0x03AE: "Allswell Inc.",
+    0x03AF: "Comm-N-Sense Corp DBA Verigo",
+    0x03B0: "VIBRADORM GmbH",
+    0x03B1: "Otodata Wireless Network Inc.",
+    0x03B2: "Propagation Systems Limited",
+    0x03B3: "Midwest Instruments & Controls",
+    0x03B4: "Alpha Nodus, inc.",
+    0x03B5: "petPOMM, Inc",
+    0x03B6: "Mattel",
+    0x03B7: "Airbly Inc.",
+    0x03B8: "A-Safe Limited",
+    0x03B9: "FREDERIQUE CONSTANT SA",
+    0x03BA: "Maxscend Microelectronics Company Limited",
+    0x03BB: "Abbott",
+    0x03BC: "ASB Bank Ltd",
+    0x03BD: "amadas",
+    0x03BE: "Applied Science, Inc.",
+    0x03BF: "iLumi Solutions Inc.",
+    0x03C0: "Arch Systems Inc.",
+    0x03C1: "Ember Technologies, Inc.",
+    0x03C2: "Snapchat Inc",
+    0x03C3: "Casambi Technologies Oy",
+    0x03C4: "Pico Technology Inc.",
+    0x03C5: "St. Jude Medical, Inc.",
+    0x03C6: "Intricon",
+    0x03C7: "Structural Health Systems, Inc.",
+    0x03C8: "Avvel International",
+    0x03C9: "Gallagher Group",
+    0x03CA: "In2things Automation Pvt. Ltd.",
+    0x03CB: "SYSDEV Srl",
+    0x03CC: "Vonkil Technologies Ltd",
+    0x03CD: "Wynd Technologies, Inc.",
+    0x03CE: "CONTRINEX S.A.",
+    0x03CF: "MIRA, Inc.",
+    0x03D0: "Watteam Ltd",
+    0x03D1: "Density Inc.",
+    0x03D2: "IOT Pot India Private Limited",
+    0x03D3: "Sigma Connectivity AB",
+    0x03D4: "PEG PEREGO SPA",
+    0x03D5: "Wyzelink Systems Inc.",
+    0x03D6: "Yota Devices LTD",
+    0x03D7: "FINSECUR",
+    0x03D8: "Zen-Me Labs Ltd",
+    0x03D9: "3IWare Co., Ltd.",
+    0x03DA: "EnOcean GmbH",
+    0x03DB: "Instabeat, Inc",
+    0x03DC: "Nima Labs",
+    0x03DD: "Andreas Stihl AG & Co. KG",
+    0x03DE: "Nathan Rhoades LLC",
+    0x03DF: "Grob Technologies, LLC",
+    0x03E0: "Actions (Zhuhai) Technology Co., Limited",
+    0x03E1: "SPD Development Company Ltd",
+    0x03E2: "Sensoan Oy",
+    0x03E3: "Qualcomm Life Inc",
+    0x03E4: "Chip-ing AG",
+    0x03E5: "ffly4u",
+    0x03E6: "IoT Instruments Oy",
+    0x03E7: "TRUE Fitness Technology",
+    0x03E8: "Reiner Kartengeraete GmbH & Co. KG.",
+    0x03E9: "SHENZHEN LEMONJOY TECHNOLOGY CO., LTD.",
+    0x03EA: "Hello Inc.",
+    0x03EB: "Evollve Inc.",
+    0x03EC: "Jigowatts Inc.",
+    0x03ED: "BASIC MICRO.COM,INC.",
+    0x03EE: "CUBE TECHNOLOGIES",
+    0x03EF: "foolography GmbH",
+    0x03F0: "CLINK",
+    0x03F1: "Hestan Smart Cooking Inc.",
+    0x03F2: "WindowMaster A/S",
+    0x03F3: "Flowscape AB",
+    0x03F4: "PAL Technologies Ltd",
+    0x03F5: "WHERE, Inc.",
+    0x03F6: "Iton Technology Corp.",
+    0x03F7: "Owl Labs Inc.",
+    0x03F8: "Rockford Corp.",
+    0x03F9: "Becon Technologies Co.,Ltd.",
+    0x03FA: "Vyassoft Technologies Inc",
+    0x03FB: "Nox Medical",
+    0x03FC: "Kimberly-Clark",
+    0x03FD: "Trimble Navigation Ltd.",
+    0x03FE: "Littelfuse",
+    0x03FF: "Withings",
+    0x0400: "i-developer IT Beratung UG",
+    0x0401: "Relations Inc.",
+    0x0402: "Sears Holdings Corporation",
+    0x0403: "Gantner Electronic GmbH",
+    0x0404: "Authomate Inc",
+    0x0405: "Vertex International, Inc.",
+    0x0406: "Airtago",
+    0x0407: "Swiss Audio SA",
+    0x0408: "ToGetHome Inc.",
+    0x0409: "AXIS",
+    0x040A: "Openmatics",
+    0x040B: "Jana Care Inc.",
+    0x040C: "Senix Corporation",
+    0x040D: "NorthStar Battery Company, LLC",
+    0x040E: "SKF (U.K.) Limited",
+    0x040F: "CO-AX Technology, Inc.",
+    0x0410: "Fender Musical Instruments",
+    0x0411: "Luidia Inc",
+    0x0412: "SEFAM",
+    0x0413: "Wireless Cables Inc",
+    0x0414: "Lightning Protection International Pty Ltd",
+    0x0415: "Uber Technologies Inc",
+    0x0416: "SODA GmbH",
+    0x0417: "Fatigue Science",
+    0x0418: "Reserved",
+    0x0419: "Novalogy LTD",
+    0x041A: "Friday Labs Limited",
+    0x041B: "OrthoAccel Technologies",
+    0x041C: "WaterGuru, Inc.",
+    0x041D: "Benning Elektrotechnik und Elektronik GmbH & Co. KG",
+    0x041E: "Dell Computer Corporation",
+    0x041F: "Kopin Corporation",
+    0x0420: "TecBakery GmbH",
+    0x0421: "Backbone Labs, Inc.",
+    0x0422: "DELSEY SA",
+    0x0423: "Chargifi Limited",
+    0x0424: "Trainesense Ltd.",
+    0x0425: "Unify Software and Solutions GmbH & Co. KG",
+    0x0426: "Husqvarna AB",
+    0x0427: "Focus fleet and fuel management inc",
+    0x0428: "SmallLoop, LLC",
+    0x0429: "Prolon Inc.",
+    0x042A: "BD Medical",
+    0x042B: "iMicroMed Incorporated",
+    0x042C: "Ticto N.V.",
+    0x042D: "Meshtech AS",
+    0x042E: "MemCachier Inc.",
+    0x042F: "Danfoss A/S",
+    0x0430: "SnapStyk Inc.",
+    0x0431: "Amway Corporation",
+    0x0432: "Silk Labs, Inc.",
+    0x0433: "Pillsy Inc.",
+    0x0434: "Hatch Baby, Inc.",
+    0x0435: "Blocks Wearables Ltd.",
+    0x0436: "Drayson Technologies (Europe) Limited",
+    0x0437: "eBest IOT Inc.",
+    0x0438: "Helvar Ltd",
+    0x0439: "Radiance Technologies",
+    0x043A: "Nuheara Limited",
+    0x043B: "Appside co., ltd.",
+    0x043C: "DeLaval",
+    0x043D: "Coiler Corporation",
+    0x043E: "Thermomedics, Inc.",
+    0x043F: "Tentacle Sync GmbH",
+    0x0440: "Valencell, Inc.",
+    0x0441: "iProtoXi Oy",
+    0x0442: "SECOM CO., LTD.",
+    0x0443: "Tucker International LLC",
+    0x0444: "Metanate Limited",
+    0x0445: "Kobian Canada Inc.",
+    0x0446: "NETGEAR, Inc.",
+    0x0447: "Fabtronics Australia Pty Ltd",
+    0x0448: "Grand Centrix GmbH",
+    0x0449: "1UP USA.com llc",
+    0x044A: "SHIMANO INC.",
+    0x044B: "Nain Inc.",
+    0x044C: "LifeStyle Lock, LLC",
+    0x044D: "VEGA Grieshaber KG",
+    0x044E: "Xtrava Inc.",
+    0x044F: "TTS Tooltechnic Systems AG & Co. KG",
+    0x0450: "Teenage Engineering AB",
+    0x0451: "Tunstall Nordic AB",
+    0x0452: "Svep Design Center AB",
+    0x0453: "Qorvo Utrecht B.V. formerly GreenPeak Technologies BV",
+    0x0454: "Sphinx Electronics GmbH & Co KG",
+    0x0455: "Atomation",
+    0x0456: "Nemik Consulting Inc",
+    0x0457: "RF INNOVATION",
+    0x0458: "Mini Solution Co., Ltd.",
+    0x0459: "Lumenetix, Inc",
+    0x045A: "2048450 Ontario Inc",
+    0x045B: "SPACEEK LTD",
+    0x045C: "Delta T Corporation",
+    0x045D: "Boston Scientific Corporation",
+    0x045E: "Nuviz, Inc.",
+    0x045F: "Real Time Automation, Inc.",
+    0x0460: "Kolibree",
+    0x0461: "vhf elektronik GmbH",
+    0x0462: "Bonsai Systems GmbH",
+    0x0463: "Fathom Systems Inc.",
+    0x0464: "Bellman & Symfon",
+    0x0465: "International Forte Group LLC",
+    0x0466: "CycleLabs Solutions inc.",
+    0x0467: "Codenex Oy",
+    0x0468: "Kynesim Ltd",
+    0x0469: "Palago AB",
+    0x046A: "INSIGMA INC.",
+    0x046B: "PMD Solutions",
+    0x046C: "Qingdao Realtime Technology Co., Ltd.",
+    0x046D: "BEGA Gantenbrink-Leuchten KG",
+    0x046E: "Pambor Ltd.",
+    0x046F: "Develco Products A/S",
+    0x0470: "iDesign s.r.l.",
+    0x0471: "TiVo Corp",
+    0x0472: "Control-J Pty Ltd",
+    0x0473: "Steelcase, Inc.",
+    0x0474: "iApartment co., ltd.",
+    0x0475: "Icom inc.",
+    0x0476: "Oxstren Wearable Technologies Private Limited",
+    0x0477: "Blue Spark Technologies",
+    0x0478: "FarSite Communications Limited",
+    0x0479: "mywerk system GmbH",
+    0x047A: "Sinosun Technology Co., Ltd.",
+    0x047B: "MIYOSHI ELECTRONICS CORPORATION",
+    0x047C: "POWERMAT LTD",
+    0x047D: "Occly LLC",
+    0x047E: "OurHub Dev IvS",
+    0x047F: "Pro-Mark, Inc.",
+    0x0480: "Dynometrics Inc.",
+    0x0481: "Quintrax Limited",
+    0x0482: "POS Tuning Udo Vosshenrich GmbH & Co. KG",
+    0x0483: "Multi Care Systems B.V.",
+    0x0484: "Revol Technologies Inc",
+    0x0485: "SKIDATA AG",
+    0x0486: "DEV TECNOLOGIA INDUSTRIA, COMERCIO E MANUTENCAO DE EQUIPAMENTOS LTDA. - ME",
+    0x0487: "Centrica Connected Home",
+    0x0488: "Automotive Data Solutions Inc",
+    0x0489: "Igarashi Engineering",
+    0x048A: "Taelek Oy",
+    0x048B: "CP Electronics Limited",
+    0x048C: "Vectronix AG",
+    0x048D: "S-Labs Sp. z o.o.",
+    0x048E: "Companion Medical, Inc.",
+    0x048F: "BlueKitchen GmbH",
+    0x0490: "Matting AB",
+    0x0491: "SOREX - Wireless Solutions GmbH",
+    0x0492: "ADC Technology, Inc.",
+    0x0493: "Lynxemi Pte Ltd",
+    0x0494: "SENNHEISER electronic GmbH & Co. KG",
+    0x0495: "LMT Mercer Group, Inc",
+    0x0496: "Polymorphic Labs LLC",
+    0x0497: "Cochlear Limited",
+    0x0498: "METER Group, Inc. USA",
+    0x0499: "Ruuvi Innovations Ltd.",
+    0x049A: "Situne AS",
+    0x049B: "nVisti, LLC",
+    0x049C: "DyOcean",
+    0x049D: "Uhlmann & Zacher GmbH",
+    0x049E: "AND!XOR LLC",
+    0x049F: "tictote AB",
+    0x04A0: "Vypin, LLC",
+    0x04A1: "PNI Sensor Corporation",
+    0x04A2: "ovrEngineered, LLC",
+    0x04A3: "GT-tronics HK Ltd",
+    0x04A4: "Herbert Waldmann GmbH & Co. KG",
+    0x04A5: "Guangzhou FiiO Electronics Technology Co.,Ltd",
+    0x04A6: "Vinetech Co., Ltd",
+    0x04A7: "Dallas Logic Corporation",
+    0x04A8: "BioTex, Inc.",
+    0x04A9: "DISCOVERY SOUND TECHNOLOGY, LLC",
+    0x04AA: "LINKIO SAS",
+    0x04AB: "Harbortronics, Inc.",
+    0x04AC: "Undagrid B.V.",
+    0x04AD: "Shure Inc",
+    0x04AE: "ERM Electronic Systems LTD",
+    0x04AF: "BIOROWER Handelsagentur GmbH",
+    0x04B0: "Weba Sport und Med. Artikel GmbH",
+    0x04B1: "Kartographers Technologies Pvt. Ltd.",
+    0x04B2: "The Shadow on the Moon",
+    0x04B3: "mobike (Hong Kong) Limited",
+    0x04B4: "Inuheat Group AB",
+    0x04B5: "Swiftronix AB",
+    0x04B6: "Diagnoptics Technologies",
+    0x04B7: "Analog Devices, Inc.",
+    0x04B8: "Soraa Inc.",
+    0x04B9: "CSR Building Products Limited",
+    0x04BA: "Crestron Electronics, Inc.",
+    0x04BB: "Neatebox Ltd",
+    0x04BC: "Draegerwerk AG & Co. KGaA",
+    0x04BD: "AlbynMedical",
+    0x04BE: "Averos FZCO",
+    0x04BF: "VIT Initiative, LLC",
+    0x04C0: "Statsports International",
+    0x04C1: "Sospitas, s.r.o.",
+    0x04C2: "Dmet Products Corp.",
+    0x04C3: "Mantracourt Electronics Limited",
+    0x04C4: "TeAM Hutchins AB",
+    0x04C5: "Seibert Williams Glass, LLC",
+    0x04C6: "Insta GmbH",
+    0x04C7: "Svantek Sp. z o.o.",
+    0x04C8: "Shanghai Flyco Electrical Appliance Co., Ltd.",
+    0x04C9: "Thornwave Labs Inc",
+    0x04CA: "Steiner-Optik GmbH",
+    0x04CB: "Novo Nordisk A/S",
+    0x04CC: "Enflux Inc.",
+    0x04CD: "Safetech Products LLC",
+    0x04CE: "GOOOLED S.R.L.",
+    0x04CF: "DOM Sicherheitstechnik GmbH & Co. KG",
+    0x04D0: "Olympus Corporation",
+    0x04D1: "KTS GmbH",
+    0x04D2: "Anloq Technologies Inc.",
+    0x04D3: "Queercon, Inc",
+    0x04D4: "5th Element Ltd",
+    0x04D5: "Gooee Limited",
+    0x04D6: "LUGLOC LLC",
+    0x04D7: "Blincam, Inc.",
+    0x04D8: "FUJIFILM Corporation",
+    0x04D9: "RandMcNally",
+    0x04DA: "Franceschi Marina snc",
+    0x04DB: "Engineered Audio, LLC.",
+    0x04DC: "IOTTIVE (OPC) PRIVATE LIMITED",
+    0x04DD: "4MOD Technology",
+    0x04DE: "Lutron Electronics Co., Inc.",
+    0x04DF: "Emerson",
+    0x04E0: "Guardtec, Inc.",
+    0x04E1: "REACTEC LIMITED",
+    0x04E2: "EllieGrid",
+    0x04E3: "Under Armour",
+    0x04E4: "Woodenshark",
+    0x04E5: "Avack Oy",
+    0x04E6: "Smart Solution Technology, Inc.",
+    0x04E7: "REHABTRONICS INC.",
+    0x04E8: "STABILO International",
+    0x04E9: "Busch Jaeger Elektro GmbH",
+    0x04EA: "Pacific Bioscience Laboratories, Inc",
+    0x04EB: "Bird Home Automation GmbH",
+    0x04EC: "Motorola Solutions",
+    0x04ED: "R9 Technology, Inc.",
+    0x04EE: "Auxivia",
+    0x04EF: "DaisyWorks, Inc",
+    0x04F0: "Kosi Limited",
+    0x04F1: "Theben AG",
+    0x04F2: "InDreamer Techsol Private Limited",
+    0x04F3: "Cerevast Medical",
+    0x04F4: "ZanCompute Inc.",
+    0x04F5: "Pirelli Tyre S.P.A.",
+    0x04F6: "McLear Limited",
+    0x04F7: "Shenzhen Huiding Technology Co.,Ltd.",
+    0x04F8: "Convergence Systems Limited",
+    0x04F9: "Interactio",
+    0x04FA: "Androtec GmbH",
+    0x04FB: "Benchmark Drives GmbH & Co. KG",
+    0x04FC: "SwingLync L. L. C.",
+    0x04FD: "Tapkey GmbH",
+    0x04FE: "Woosim Systems Inc.",
+    0x04FF: "Microsemi Corporation",
+    0x0500: "Wiliot LTD.",
+    0x0501: "Polaris IND",
+    0x0502: "Specifi-Kali LLC",
+    0x0503: "Locoroll, Inc",
+    0x0504: "PHYPLUS Inc",
+    0x0505: "Inplay Technologies LLC",
+    0x0506: "Hager",
+    0x0507: "Yellowcog",
+    0x0508: "Axes System sp. z o. o.",
+    0x0509: "myLIFTER Inc.",
+    0x050A: "Shake-on B.V.",
+    0x050B: "Vibrissa Inc.",
+    0x050C: "OSRAM GmbH",
+    0x050D: "TRSystems GmbH",
+    0x050E: "Yichip Microelectronics (Hangzhou) Co.,Ltd.",
+    0x050F: "Foundation Engineering LLC",
+    0x0510: "UNI-ELECTRONICS, INC.",
+    0x0511: "Brookfield Equinox LLC",
+    0x0512: "Soprod SA",
+    0x0513: "9974091 Canada Inc.",
+    0x0514: "FIBRO GmbH",
+    0x0515: "RB Controls Co., Ltd.",
+    0x0516: "Footmarks",
+    0x0517: "Amtronic Sverige AB (formerly Amcore AB)",
+    0x0518: "MAMORIO.inc",
+    0x0519: "Tyto Life LLC",
+    0x051A: "Leica Camera AG",
+    0x051B: "Angee Technologies Ltd.",
+    0x051C: "EDPS",
+    0x051D: "OFF Line Co., Ltd.",
+    0x051E: "Detect Blue Limited",
+    0x051F: "Setec Pty Ltd",
+    0x0520: "Target Corporation",
+    0x0521: "IAI Corporation",
+    0x0522: "NS Tech, Inc.",
+    0x0523: "MTG Co., Ltd.",
+    0x0524: "Hangzhou iMagic Technology Co., Ltd",
+    0x0525: "HONGKONG NANO IC TECHNOLOGIES  CO., LIMITED",
+    0x0526: "Honeywell International Inc.",
+    0x0527: "Albrecht JUNG",
+    0x0528: "Lunera Lighting Inc.",
+    0x0529: "Lumen UAB",
+    0x052A: "Keynes Controls Ltd",
+    0x052B: "Novartis AG",
+    0x052C: "Geosatis SA",
+    0x052D: "EXFO, Inc.",
+    0x052E: "LEDVANCE GmbH",
+    0x052F: "Center ID Corp.",
+    0x0530: "Adolene, Inc.",
+    0x0531: "D&M Holdings Inc.",
+    0x0532: "CRESCO Wireless, Inc.",
+    0x0533: "Nura Operations Pty Ltd",
+    0x0534: "Frontiergadget, Inc.",
+    0x0535: "Smart Component Technologies Limited",
+    0x0536: "ZTR Control Systems LLC",
+    0x0537: "MetaLogics Corporation",
+    0x0538: "Medela AG",
+    0x0539: "OPPLE Lighting Co., Ltd",
+    0x053A: "Savitech Corp.,",
+    0x053B: "prodigy",
+    0x053C: "Screenovate Technologies Ltd",
+    0x053D: "TESA SA",
+    0x053E: "CLIM8 LIMITED",
+    0x053F: "Silergy Corp",
+    0x0540: "SilverPlus, Inc",
+    0x0541: "Sharknet srl",
+    0x0542: "Mist Systems, Inc.",
+    0x0543: "MIWA LOCK CO.,Ltd",
+    0x0544: "OrthoSensor, Inc.",
+    0x0545: "Candy Hoover Group s.r.l",
+    0x0546: "Apexar Technologies S.A.",
+    0x0547: "LOGICDATA d.o.o.",
+    0x0548: "Knick Elektronische Messgeraete GmbH & Co. KG",
+    0x0549: "Smart Technologies and Investment Limited",
+    0x054A: "Linough Inc.",
+    0x054B: "Advanced Electronic Designs, Inc.",
+    0x054C: "Carefree Scott Fetzer Co Inc",
+    0x054D: "Sensome",
+    0x054E: "FORTRONIK storitve d.o.o.",
+    0x054F: "Sinnoz",
+    0x0550: "Versa Networks, Inc.",
+    0x0551: "Sylero",
+    0x0552: "Avempace SARL",
+    0x0553: "Nintendo Co., Ltd.",
+    0x0554: "National Instruments",
+    0x0555: "KROHNE Messtechnik GmbH",
+    0x0556: "Otodynamics Ltd",
+    0x0557: "Arwin Technology Limited",
+    0x0558: "benegear, inc.",
+    0x0559: "Newcon Optik",
+    0x055A: "CANDY HOUSE, Inc.",
+    0x055B: "FRANKLIN TECHNOLOGY INC",
+    0x055C: "Lely",
+    0x055D: "Valve Corporation",
+    0x055E: "Hekatron Vertriebs GmbH",
+    0x055F: "PROTECH S.A.S. DI GIRARDI ANDREA & C.",
+    0x0560: "Sarita CareTech APS (formerly Sarita CareTech IVS)",
+    0x0561: "Finder S.p.A.",
+    0x0562: "Thalmic Labs Inc.",
+    0x0563: "Steinel Vertrieb GmbH",
+    0x0564: "Beghelli Spa",
+    0x0565: "Beijing Smartspace Technologies Inc.",
+    0x0566: "CORE TRANSPORT TECHNOLOGIES NZ LIMITED",
+    0x0567: "Xiamen Everesports Goods Co., Ltd",
+    0x0568: "Bodyport Inc.",
+    0x0569: "Audionics System, INC.",
+    0x056A: "Flipnavi Co.,Ltd.",
+    0x056B: "Rion Co., Ltd.",
+    0x056C: "Long Range Systems, LLC",
+    0x056D: "Redmond Industrial Group LLC",
+    0x056E: "VIZPIN INC.",
+    0x056F: "BikeFinder AS",
+    0x0570: "Consumer Sleep Solutions LLC",
+    0x0571: "PSIKICK, INC.",
+    0x0572: "AntTail.com",
+    0x0573: "Lighting Science Group Corp.",
+    0x0574: "AFFORDABLE ELECTRONICS INC",
+    0x0575: "Integral Memroy Plc",
+    0x0576: "Globalstar, Inc.",
+    0x0577: "True Wearables, Inc.",
+    0x0578: "Wellington Drive Technologies Ltd",
+    0x0579: "Ensemble Tech Private Limited",
+    0x057A: "OMNI Remotes",
+    0x057B: "Duracell U.S. Operations Inc.",
+    0x057C: "Toor Technologies LLC",
+    0x057D: "Instinct Performance",
+    0x057E: "Beco, Inc",
+    0x057F: "Scuf Gaming International, LLC",
+    0x0580: "ARANZ Medical Limited",
+    0x0581: "LYS TECHNOLOGIES LTD",
+    0x0582: "Breakwall Analytics, LLC",
+    0x0583: "Code Blue Communications",
+    0x0584: "Gira Giersiepen GmbH & Co. KG",
+    0x0585: "Hearing Lab Technology",
+    0x0586: "LEGRAND",
+    0x0587: "Derichs GmbH",
+    0x0588: "ALT-TEKNIK LLC",
+    0x0589: "Star Technologies",
+    0x058A: "START TODAY CO.,LTD.",
+    0x058B: "Maxim Integrated Products",
+    0x058C: "MERCK Kommanditgesellschaft auf Aktien",
+    0x058D: "Jungheinrich Aktiengesellschaft",
+    0x058E: "Oculus VR, LLC",
+    0x058F: "HENDON SEMICONDUCTORS PTY LTD",
+    0x0590: "Pur3 Ltd",
+    0x0591: "Viasat Group S.p.A.",
+    0x0592: "IZITHERM",
+    0x0593: "Spaulding Clinical Research",
+    0x0594: "Kohler Company",
+    0x0595: "Inor Process AB",
+    0x0596: "My Smart Blinds",
+    0x0597: "RadioPulse Inc",
+    0x0598: "rapitag GmbH",
+    0x0599: "Lazlo326, LLC.",
+    0x059A: "Teledyne Lecroy, Inc.",
+    0x059B: "Dataflow Systems Limited",
+    0x059C: "Macrogiga Electronics",
+    0x059D: "Tandem Diabetes Care",
+    0x059E: "Polycom, Inc.",
+    0x059F: "Fisher & Paykel Healthcare",
+    0x05A0: "RCP Software Oy",
+    0x05A1: "Shanghai Xiaoyi Technology Co.,Ltd.",
+    0x05A2: "ADHERIUM(NZ) LIMITED",
+    0x05A3: "Axiomware Systems Incorporated",
+    0x05A4: "O. E. M. Controls, Inc.",
+    0x05A5: "Kiiroo BV",
+    0x05A6: "Telecon Mobile Limited",
+    0x05A7: "Sonos Inc",
+    0x05A8: "Tom Allebrandi Consulting",
+    0x05A9: "Monidor",
+    0x05AA: "Tramex Limited",
+    0x05AB: "Nofence AS",
+    0x05AC: "GoerTek Dynaudio Co., Ltd.",
+    0x05AD: "INIA",
+    0x05AE: "CARMATE MFG.CO.,LTD",
+    0x05AF: "OV LOOP, INC. (formerly ONvocal)",
+    0x05B0: "NewTec GmbH",
+    0x05B1: "Medallion Instrumentation Systems",
+    0x05B2: "CAREL INDUSTRIES S.P.A.",
+    0x05B3: "Parabit Systems, Inc.",
+    0x05B4: "White Horse Scientific ltd",
+    0x05B5: "verisilicon",
+    0x05B6: "Elecs Industry Co.,Ltd.",
+    0x05B7: "Beijing Pinecone Electronics Co.,Ltd.",
+    0x05B8: "Ambystoma Labs Inc.",
+    0x05B9: "Suzhou Pairlink Network Technology",
+    0x05BA: "igloohome",
+    0x05BB: "Oxford Metrics plc",
+    0x05BC: "Leviton Mfg. Co., Inc.",
+    0x05BD: "ULC Robotics Inc.",
+    0x05BE: "RFID Global by Softwork SrL",
+    0x05BF: "Real-World-Systems Corporation",
+    0x05C0: "Nalu Medical, Inc.",
+    0x05C1: "P.I.Engineering",
+    0x05C2: "Grote Industries",
+    0x05C3: "Runtime, Inc.",
+    0x05C4: "Codecoup sp. z o.o. sp. k.",
+    0x05C5: "SELVE GmbH & Co. KG",
+    0x05C6: "Smart Animal Training Systems, LLC",
+    0x05C7: "Lippert Components, INC",
+    0x05C8: "SOMFY SAS",
+    0x05C9: "TBS Electronics B.V.",
+    0x05CA: "MHL Custom Inc",
+    0x05CB: "LucentWear LLC",
+    0x05CC: "WATTS ELECTRONICS",
+    0x05CD: "RJ Brands LLC",
+    0x05CE: "V-ZUG Ltd",
+    0x05CF: "Biowatch SA",
+    0x05D0: "Anova Applied Electronics",
+    0x05D1: "Lindab AB",
+    0x05D2: "frogblue TECHNOLOGY GmbH",
+    0x05D3: "Acurable Limited",
+    0x05D4: "LAMPLIGHT Co., Ltd.",
+    0x05D5: "TEGAM, Inc.",
+    0x05D6: "Zhuhai Jieli technology Co.,Ltd",
+    0x05D7: "modum.io AG",
+    0x05D8: "Farm Jenny LLC",
+    0x05D9: "Toyo Electronics Corporation",
+    0x05DA: "Applied Neural Research Corp",
+    0x05DB: "Avid Identification Systems, Inc.",
+    0x05DC: "Petronics Inc.",
+    0x05DD: "essentim GmbH",
+    0x05DE: "QT Medical INC.",
+    0x05DF: "VIRTUALCLINIC.DIRECT LIMITED",
+    0x05E0: "Viper Design LLC",
+    0x05E1: "Human, Incorporated",
+    0x05E2: "stAPPtronics GmbH",
+    0x05E3: "Elemental Machines, Inc.",
+    0x05E4: "Taiyo Yuden Co., Ltd",
+    0x05E5: "INEO ENERGY& SYSTEMS",
+    0x05E6: "Motion Instruments Inc.",
+    0x05E7: "PressurePro",
+    0x05E8: "COWBOY",
+    0x05E9: "iconmobile GmbH",
+    0x05EA: "ACS-Control-System GmbH",
+    0x05EB: "Bayerische Motoren Werke AG",
+    0x05EC: "Gycom Svenska AB",
+    0x05ED: "Fuji Xerox Co., Ltd",
+    0x05EE: "Glide Inc.",
+    0x05EF: "SIKOM AS",
+    0x05F0: "beken",
+    0x05F1: "The Linux Foundation",
+    0x05F2: "Try and E CO.,LTD.",
+    0x05F3: "SeeScan",
+    0x05F4: "Clearity, LLC",
+    0x05F5: "GS TAG",
+    0x05F6: "DPTechnics",
+    0x05F7: "TRACMO, INC.",
+    0x05F8: "Anki Inc.",
+    0x05F9: "Hagleitner Hygiene International GmbH",
+    0x05FA: "Konami Sports Life Co., Ltd.",
+    0x05FB: "Arblet Inc.",
+    0x05FC: "Masbando GmbH",
+    0x05FD: "Innoseis",
+    0x05FE: "Niko nv",
+    0x05FF: "Wellnomics Ltd",
+    0x0600: "iRobot Corporation",
+    0x0601: "Schrader Electronics",
+    0x0602: "Geberit International AG",
+    0x0603: "Fourth Evolution Inc",
+    0x0604: "Cell2Jack LLC",
+    0x0605: "FMW electronic Futterer u. Maier-Wolf OHG",
+    0x0606: "John Deere",
+    0x0607: "Rookery Technology Ltd",
+    0x0608: "KeySafe-Cloud",
+    0x0609: "BUCHI Labortechnik AG",
+    0x060A: "IQAir AG",
+    0x060B: "Triax Technologies Inc",
+    0x060C: "Vuzix Corporation",
+    0x060D: "TDK Corporation",
+    0x060E: "Blueair AB",
+    0x060F: "Signify Netherlands",
+    0x0610: "ADH GUARDIAN USA LLC",
+    0x0611: "Beurer GmbH",
+    0x0612: "Playfinity AS",
+    0x0613: "Hans Dinslage GmbH",
+    0x0614: "OnAsset Intelligence, Inc.",
+    0x0615: "INTER ACTION Corporation",
+    0x0616: "OS42 UG (haftungsbeschraenkt)",
+    0x0617: "WIZCONNECTED COMPANY LIMITED",
+    0x0618: "Audio-Technica Corporation",
+    0x0619: "Six Guys Labs, s.r.o.",
+    0x061A: "R.W. Beckett Corporation",
+    0x061B: "silex technology, inc.",
+    0x061C: "Univations Limited",
+    0x061D: "SENS Innovation ApS",
+    0x061E: "Diamond Kinetics, Inc.",
+    0x061F: "Phrame Inc.",
+    0x0620: "Forciot Oy",
+    0x0621: "Noordung d.o.o.",
+    0x0622: "Beam Labs, LLC",
+    0x0623: "Philadelphia Scientific (U.K.) Limited",
+    0x0624: "Biovotion AG",
+    0x0625: "Square Panda, Inc.",
+    0x0626: "Amplifico",
+    0x0627: "WEG S.A.",
+    0x0628: "Ensto Oy",
+    0x0629: "PHONEPE PVT LTD",
+    0x062A: "Lunatico Astronomia SL",
+    0x062B: "MinebeaMitsumi Inc.",
+    0x062C: "ASPion GmbH",
+    0x062D: "Vossloh-Schwabe Deutschland GmbH",
+    0x062E: "Procept",
+    0x062F: "ONKYO Corporation",
+    0x0630: "Asthrea D.O.O.",
+    0x0631: "Fortiori Design LLC",
+    0x0632: "Hugo Muller GmbH & Co KG",
+    0x0633: "Wangi Lai PLT",
+    0x0634: "Fanstel Corp",
+    0x0635: "Crookwood",
+    0x0636: "ELECTRONICA INTEGRAL DE SONIDO S.A.",
+    0x0637: "GiP Innovation Tools GmbH",
+    0x0638: "LX SOLUTIONS PTY LIMITED",
+    0x0639: "Shenzhen Minew Technologies Co., Ltd.",
+    0x063A: "Prolojik Limited",
+    0x063B: "Kromek Group Plc",
+    0x063C: "Contec Medical Systems Co., Ltd.",
+    0x063D: "Xradio Technology Co.,Ltd.",
+    0x063E: "The Indoor Lab, LLC",
+    0x063F: "LDL TECHNOLOGY",
+    0x0640: "Parkifi",
+    0x0641: "Revenue Collection Systems FRANCE SAS",
+    0x0642: "Bluetrum Technology Co.,Ltd",
+    0x0643: "makita corporation",
+    0x0644: "Apogee Instruments",
+    0x0645: "BM3",
+    0x0646: "SGV Group Holding GmbH & Co. KG",
+    0x0647: "MED-EL",
+    0x0648: "Ultune Technologies",
+    0x0649: "Ryeex Technology Co.,Ltd.",
+    0x064A: "Open Research Institute, Inc.",
+    0x064B: "Scale-Tec, Ltd",
+    0x064C: "Zumtobel Group AG",
+    0x064D: "iLOQ Oy",
+    0x064E: "KRUXWorks Technologies Private Limited",
+    0x064F: "Digital Matter Pty Ltd",
+    0x0650: "Coravin, Inc.",
+    0x0651: "Stasis Labs, Inc.",
+    0x0652: "ITZ Innovations- und Technologiezentrum GmbH",
+    0x0653: "Meggitt SA",
+    0x0654: "Ledlenser GmbH & Co. KG",
+    0x0655: "Renishaw PLC",
+    0x0656: "ZhuHai AdvanPro Technology Company Limited",
+    0x0657: "Meshtronix Limited",
+    0x0658: "Payex Norge AS",
+    0x0659: "UnSeen Technologies Oy",
+    0x065A: "Zound Industries International AB",
+    0x065B: "Sesam Solutions BV",
+    0x065C: "PixArt Imaging Inc.",
+    0x065D: "Panduit Corp.",
+    0x065E: "Alo AB",
+    0x065F: "Ricoh Company Ltd",
+    0x0660: "RTC Industries, Inc.",
+    0x0661: "Mode Lighting Limited",
+    0x0662: "Particle Industries, Inc.",
+    0x0663: "Advanced Telemetry Systems, Inc.",
+    0x0664: "RHA TECHNOLOGIES LTD",
+    0x0665: "Pure International Limited",
+    0x0666: "WTO Werkzeug-Einrichtungen GmbH",
+    0x0667: "Spark Technology Labs Inc.",
+    0x0668: "Bleb Technology srl",
+    0x0669: "Livanova USA, Inc.",
+    0x066A: "Brady Worldwide Inc.",
+    0x066B: "DewertOkin GmbH",
+    0x066C: "Ztove ApS",
+    0x066D: "Venso EcoSolutions AB",
+    0x066E: "Eurotronik Kranj d.o.o.",
+    0x066F: "Hug Technology Ltd",
+    0x0670: "Gema Switzerland GmbH",
+    0x0671: "Buzz Products Ltd.",
+    0x0672: "Kopi",
+    0x0673: "Innova Ideas Limited",
+    0x0674: "BeSpoon",
+    0x0675: "Deco Enterprises, Inc.",
+    0x0676: "Expai Solutions Private Limited",
+    0x0677: "Innovation First, Inc.",
+    0x0678: "SABIK Offshore GmbH",
+    0x0679: "4iiii Innovations Inc.",
+    0x067A: "The Energy Conservatory, Inc.",
+    0x067B: "I.FARM, INC.",
+    0x067C: "Tile, Inc.",
+    0x067D: "Form Athletica Inc.",
+    0x067E: "MbientLab Inc",
+    0x067F: "NETGRID S.N.C. DI BISSOLI MATTEO, CAMPOREALE SIMONE, TOGNETTI FEDERICO",
+    0x0680: "Mannkind Corporation",
+    0x0681: "Trade FIDES a.s.",
+    0x0682: "Photron Limited",
+    0x0683: "Eltako GmbH",
+    0x0684: "Dermalapps, LLC",
+    0x0685: "Greenwald Industries",
+    0x0686: "inQs Co., Ltd.",
+    0x0687: "Cherry GmbH",
+    0x0688: "Amsted Digital Solutions Inc.",
+    0x0689: "Tacx b.v.",
+    0x068A: "Raytac Corporation",
+    0x068B: "Jiangsu Teranovo Tech Co., Ltd.",
+    0x068C: "Changzhou Sound Dragon Electronics and Acoustics Co., Ltd",
+    0x068D: "JetBeep Inc.",
+    0x068E: "Razer Inc.",
+    0x068F: "JRM Group Limited",
+    0x0690: "Eccrine Systems, Inc.",
+    0x0691: "Curie Point AB",
+    0x0692: "Georg Fischer AG",
+    0x0693: "Hach - Danaher",
+    0x0694: "T&A Laboratories LLC",
+    0x0695: "Koki Holdings Co., Ltd.",
+    0x0696: "Gunakar Private Limited",
+    0x0697: "Stemco Products Inc",
+    0x0698: "Wood IT Security, LLC",
+    0x0699: "RandomLab SAS",
+    0x069A: "Adero, Inc. (formerly as TrackR, Inc.)",
+    0x069B: "Dragonchip Limited",
+    0x069C: "Noomi AB",
+    0x069D: "Vakaros LLC",
+    0x069E: "Delta Electronics, Inc.",
+    0x069F: "FlowMotion Technologies AS",
+    0x06A0: "OBIQ Location Technology Inc.",
+    0x06A1: "Cardo Systems, Ltd",
+    0x06A2: "Globalworx GmbH",
+    0x06A3: "Nymbus, LLC",
+    0x06A4: "Sanyo Techno Solutions Tottori Co., Ltd.",
+    0x06A5: "TEKZITEL PTY LTD",
+    0x06A6: "Roambee Corporation",
+    0x06A7: "Chipsea Technologies (ShenZhen) Corp.",
+    0x06A8: "GD Midea Air-Conditioning Equipment Co., Ltd.",
+    0x06A9: "Soundmax Electronics Limited",
+    0x06AA: "Produal Oy",
+    0x06AB: "HMS Industrial Networks AB",
+    0x06AC: "Ingchips Technology Co., Ltd.",
+    0x06AD: "InnovaSea Systems Inc.",
+    0x06AE: "SenseQ Inc.",
+    0x06AF: "Shoof Technologies",
+    0x06B0: "BRK Brands, Inc.",
+    0x06B1: "SimpliSafe, Inc.",
+    0x06B2: "Tussock Innovation 2013 Limited",
+    0x06B3: "The Hablab ApS",
+    0x06B4: "Sencilion Oy",
+    0x06B5: "Wabilogic Ltd.",
+    0x06B6: "Sociometric Solutions, Inc.",
+    0x06B7: "iCOGNIZE GmbH",
+    0x06B8: "ShadeCraft, Inc",
+    0x06B9: "Beflex Inc.",
+    0x06BA: "Beaconzone Ltd",
+    0x06BB: "Leaftronix Analogic Solutions Private Limited",
+    0x06BC: "TWS Srl",
+    0x06BD: "ABB Oy",
+    0x06BE: "HitSeed Oy",
+    0x06BF: "Delcom Products Inc.",
+    0x06C0: "CAME S.p.A.",
+    0x06C1: "Alarm.com Holdings, Inc",
+    0x06C2: "Measurlogic Inc.",
+    0x06C3: "King I Electronics.Co.,Ltd",
+    0x06C4: "Dream Labs GmbH",
+    0x06C5: "Urban Compass, Inc",
+    0x06C6: "Simm Tronic Limited",
+    0x06C7: "Somatix Inc",
+    0x06C8: "Storz & Bickel GmbH & Co. KG",
+    0x06C9: "MYLAPS B.V.",
+    0x06CA: "Shenzhen Zhongguang Infotech Technology Development Co., Ltd",
+    0x06CB: "Dyeware, LLC",
+    0x06CC: "Dongguan SmartAction Technology Co.,Ltd.",
+    0x06CD: "DIG Corporation",
+    0x06CE: "FIOR & GENTZ",
+    0x06CF: "Belparts N.V.",
+    0x06D0: "Etekcity Corporation",
+    0x06D1: "Meyer Sound Laboratories, Incorporated",
+    0x06D2: "CeoTronics AG",
+    0x06D3: "TriTeq Lock and Security, LLC",
+    0x06D4: "DYNAKODE TECHNOLOGY PRIVATE LIMITED",
+    0x06D5: "Sensirion AG",
+    0x06D6: "JCT Healthcare Pty Ltd",
+    0x06D7: "FUBA Automotive Electronics GmbH",
+    0x06D8: "AW Company",
+    0x06D9: "Shanghai Mountain View Silicon Co.,Ltd.",
+    0x06DA: "Zliide Technologies ApS",
+    0x06DB: "Automatic Labs, Inc.",
+    0x06DC: "Industrial Network Controls, LLC",
+    0x06DD: "Intellithings Ltd.",
+    0x06DE: "Navcast, Inc.",
+    0x06DF: "Hubbell Lighting, Inc.",
+    0x06E0: "Avaya ",
+    0x06E1: "Milestone AV Technologies LLC",
+    0x06E2: "Alango Technologies Ltd",
+    0x06E3: "Spinlock Ltd",
+    0x06E4: "Aluna",
+    0x06E5: "OPTEX CO.,LTD.",
+    0x06E6: "NIHON DENGYO KOUSAKU",
+    0x06E7: "VELUX A/S",
+    0x06E8: "Almendo Technologies GmbH",
+    0x06E9: "Zmartfun Electronics, Inc.",
+    0x06EA: "SafeLine Sweden AB",
+    0x06EB: "Houston Radar LLC",
+    0x06EC: "Sigur",
+    0x06ED: "J Neades Ltd",
+    0x06EE: "Avantis Systems Limited",
+    0x06EF: "ALCARE Co., Ltd.",
+    0x06F0: "Chargy Technologies, SL",
+    0x06F1: "Shibutani Co., Ltd.",
+    0x06F2: "Trapper Data AB",
+    0x06F3: "Alfred International Inc.",
+    0x06F4: "Near Field Solutions Ltd",
+    0x06F5: "Vigil Technologies Inc.",
+    0x06F6: "Vitulo Plus BV",
+    0x06F7: "WILKA Schliesstechnik GmbH",
+    0x06F8: "BodyPlus Technology Co.,Ltd",
+    0x06F9: "happybrush GmbH",
+    0x06FA: "Enequi AB",
+    0x06FB: "Sartorius AG",
+    0x06FC: "Tom Communication Industrial Co.,Ltd.",
+    0x06FD: "ESS Embedded System Solutions Inc.",
+    0x06FE: "Mahr GmbH",
+    0x06FF: "Redpine Signals Inc",
+    0x0700: "TraqFreq LLC",
+    0x0701: "PAFERS TECH",
+    0x0702: "Akciju sabiedriba \"SAF TEHNIKA\"",
+    0x0703: "Beijing Jingdong Century Trading Co., Ltd.",
+    0x0704: "JBX Designs Inc.",
+    0x0705: "AB Electrolux",
+    0x0706: "Wernher von Braun Center for ASdvanced Research",
+    0x0707: "Essity Hygiene and Health Aktiebolag",
+    0x0708: "Be Interactive Co., Ltd",
+    0x0709: "Carewear Corp.",
+    0x070A: "Huf Hülsbeck & Fürst GmbH & Co. KG",
+    0x070B: "Element Products, Inc.",
+    0x070C: "Beijing Winner Microelectronics Co.,Ltd",
+    0x070D: "SmartSnugg Pty Ltd",
+    0x070E: "FiveCo Sarl",
+    0x070F: "California Things Inc.",
+    0x0710: "Audiodo AB",
+    0x0711: "ABAX AS",
+    0x0712: "Bull Group Company Limited",
+    0x0713: "Respiri Limited",
+    0x0714: "MindPeace Safety LLC",
+    0x0715: "Vgyan Solutions",
+    0x0716: "Altonics",
+    0x0717: "iQsquare BV",
+    0x0718: "IDIBAIX enginneering",
+    0x0719: "ECSG",
+    0x071A: "REVSMART WEARABLE HK CO LTD",
+    0x071B: "Precor",
+    0x071C: "F5 Sports, Inc",
+    0x071D: "exoTIC Systems",
+    0x071E: "DONGGUAN HELE ELECTRONICS CO., LTD",
+    0x071F: "Dongguan Liesheng Electronic Co.Ltd",
+    0x0720: "Oculeve, Inc.",
+    0x0721: "Clover Network, Inc.",
+    0x0722: "Xiamen Eholder Electronics Co.Ltd",
+    0x0723: "Ford Motor Company",
+    0x0724: "Guangzhou SuperSound Information Technology Co.,Ltd",
+    0x0725: "Tedee Sp. z o.o.",
+    0x0726: "PHC Corporation",
+    0x0727: "STALKIT AS",
+    0x0728: "Eli Lilly and Company",
+    0x0729: "SwaraLink Technologies",
+    0x072A: "JMR embedded systems GmbH",
+    0x072B: "Bitkey Inc.",
+    0x072C: "GWA Hygiene GmbH",
+    0x072D: "Safera Oy",
+    0x072E: "Open Platform Systems LLC",
+    0x072F: "OnePlus Electronics (Shenzhen) Co., Ltd.",
+    0x0730: "Wildlife Acoustics, Inc.",
+    0x0731: "ABLIC Inc.",
+    0x0732: "Dairy Tech, Inc.",
+    0x0733: "Iguanavation, Inc.",
+    0x0734: "DiUS Computing Pty Ltd",
+    0x0735: "UpRight Technologies LTD",
+    0x0736: "FrancisFund, LLC",
+    0x0737: "LLC Navitek",
+    0x0738: "Glass Security Pte Ltd",
+    0x0739: "Jiangsu Qinheng Co., Ltd.",
+    0x073A: "Chandler Systems Inc.",
+    0x073B: "Fantini Cosmi s.p.a.",
+    0x073C: "Acubit ApS",
+    0x073D: "Beijing Hao Heng Tian Tech Co., Ltd.",
+    0x073E: "Bluepack S.R.L.",
+    0x073F: "Beijing Unisoc Technologies Co., Ltd.",
+    0x0740: "HITIQ LIMITED",
+    0x0741: "MAC SRL",
+    0x0742: "DML LLC",
+    0x0743: "Sanofi",
+    0x0744: "SOCOMEC",
+    0x0745: "WIZNOVA, Inc.",
+    0x0746: "Seitec Elektronik GmbH",
+    0x0747: "OR Technologies Pty Ltd",
+    0x0748: "GuangZhou KuGou Computer Technology Co.Ltd",
+    0x0749: "DIAODIAO (Beijing) Technology Co., Ltd.",
+    0x074A: "Illusory Studios LLC",
+    0x074B: "Sarvavid Software Solutions LLP",
+    0x074C: "iopool s.a.",
+    0x074D: "Amtech Systems, LLC",
+    0x074E: "EAGLE DETECTION SA",
+    0x074F: "MEDIATECH S.R.L.",
+    0x0750: "Hamilton Professional Services of Canada Incorporated",
+    0x0751: "Changsha JEMO IC Design Co.,Ltd",
+    0x0752: "Elatec GmbH",
+    0x0753: "JLG Industries, Inc.",
+    0x0754: "Michael Parkin",
+    0x0755: "Brother Industries, Ltd",
+    0x0756: "Lumens For Less, Inc",
+    0x0757: "ELA Innovation",
+    0x0758: "umanSense AB",
+    0x0759: "Shanghai InGeek Cyber Security Co., Ltd.",
+    0x075A: "HARMAN CO.,LTD.",
+    0x075B: "Smart Sensor Devices AB",
+    0x075C: "Antitronics Inc.",
+    0x075D: "RHOMBUS SYSTEMS, INC.",
+    0x075E: "Katerra Inc.",
+    0x075F: "Remote Solution Co., LTD.",
+    0x0760: "Vimar SpA",
+    0x0761: "Mantis Tech LLC",
+    0x0762: "TerOpta Ltd",
+    0x0763: "PIKOLIN S.L.",
+    0x0764: "WWZN Information Technology Company Limited",
+    0x0765: "Voxx International",
+    0x0766: "ART AND PROGRAM, INC.",
+    0x0767: "NITTO DENKO ASIA TECHNICAL CENTRE PTE. LTD.",
+    0x0768: "Peloton Interactive Inc.",
+    0x0769: "Force Impact Technologies",
+    0x076A: "Dmac Mobile Developments, LLC",
+    0x076B: "Engineered Medical Technologies",
+    0x076C: "Noodle Technology inc",
+    0x076D: "Graesslin GmbH",
+    0x076E: "WuQi technologies, Inc.",
+    0x076F: "Successful Endeavours Pty Ltd",
+    0x0770: "InnoCon Medical ApS",
+    0x0771: "Corvex Connected Safety",
+    0x0772: "Thirdwayv Inc.",
+    0x0773: "Echoflex Solutions Inc.",
+    0x0774: "C-MAX Asia Limited",
+    0x0775: "4eBusiness GmbH",
+    0x0776: "Cyber Transport Control GmbH",
+    0x0777: "Cue",
+    0x0778: "KOAMTAC INC.",
+    0x0779: "Loopshore Oy",
+    0x077A: "Niruha Systems Private Limited",
+    0x077B: "AmaterZ, Inc.",
+    0x077C: "radius co., ltd.",
+    0x077D: "Sensority, s.r.o.",
+    0x077E: "Sparkage Inc.",
+    0x077F: "Glenview Software Corporation",
+    0x0780: "Finch Technologies Ltd.",
+    0x0781: "Qingping Technology (Beijing) Co., Ltd.",
+    0x0782: "DeviceDrive AS",
+    0x0783: "ESEMBER LIMITED LIABILITY COMPANY",
+    0x0784: "audifon GmbH & Co. KG",
+    0x0785: "O2 Micro, Inc.",
+    0x0786: "HLP Controls Pty Limited",
+    0x0787: "Pangaea Solution",
+    0x0788: "BubblyNet, LLC",
+    0x078A: "The Wildflower Foundation",
+    0x078B: "Optikam Tech Inc.",
+    0x078C: "MINIBREW HOLDING B.V",
+    0x078D: "Cybex GmbH",
+    0x078E: "FUJIMIC NIIGATA, INC.",
+    0x078F: "Hanna Instruments, Inc.",
+    0x0790: "KOMPAN A/S",
+    0x0791: "Scosche Industries, Inc.",
+    0x0792: "Provo Craft",
+    0x0793: "AEV spol. s r.o.",
+    0x0794: "The Coca-Cola Company",
+    0x0795: "GASTEC CORPORATION",
+    0x0796: "StarLeaf Ltd",
+    0x0797: "Water-i.d. GmbH",
+    0x0798: "HoloKit, Inc.",
+    0x0799: "PlantChoir Inc.",
+    0x079A: "GuangDong Oppo Mobile Telecommunications Corp., Ltd.",
+    0x079B: "CST ELECTRONICS (PROPRIETARY) LIMITED",
+    0x079C: "Sky UK Limited",
+    0x079D: "Digibale Pty Ltd",
+    0x079E: "Smartloxx GmbH",
+    0x079F: "Pune Scientific LLP",
+    0x07A0: "Regent Beleuchtungskorper AG",
+    0x07A1: "Apollo Neuroscience, Inc.",
+    0x07A2: "Roku, Inc.",
+    0x07A3: "Comcast Cable",
+    0x07A4: "Xiamen Mage Information Technology Co., Ltd.",
+    0x07A5: "RAB Lighting, Inc.",
+    0x07A6: "Musen Connect, Inc.",
+    0x07A7: "Zume, Inc.",
+    0x07A8: "conbee GmbH",
+    0x07A9: "Bruel & Kjaer Sound & Vibration",
+    0x07AA: "The Kroger Co.",
+    0x07AB: "Granite River Solutions, Inc.",
+    0x07AC: "LoupeDeck Oy",
+    0x07AD: "New H3C Technologies Co.,Ltd",
+    0x07AE: "Aurea Solucoes Tecnologicas Ltda.",
+    0x07AF: "Hong Kong Bouffalo Lab Limited",
+    0x07B0: "GV Concepts Inc.",
+    0x07B1: "Thomas Dynamics, LLC",
+    0x07B2: "Moeco IOT Inc.",
+    0x07B3: "2N TELEKOMUNIKACE a.s.",
+    0x07B4: "Hormann KG Antriebstechnik",
+    0x07B5: "CRONO CHIP, S.L.",
+    0x07B6: "Soundbrenner Limited",
+    0x07B7: "ETABLISSEMENTS GEORGES RENAULT",
+    0x07B8: "iSwip",
+    0x07B9: "Epona Biotec Limited",
+    0x07BA: "Battery-Biz Inc.",
+    0x07BB: "EPIC S.R.L.",
+    0x07BC: "KD CIRCUITS LLC",
+    0x07BD: "Genedrive Diagnostics Ltd",
+    0x07BE: "Axentia Technologies AB",
+    0x07BF: "REGULA Ltd.",
+    0x07C0: "Biral AG",
+    0x07C1: "A.W. Chesterton Company",
+    0x07C2: "Radinn AB",
+    0x07C3: "CIMTechniques, Inc.",
+    0x07C4: "Johnson Health Tech NA",
+    0x07C5: "June Life, Inc.",
+    0x07C6: "Bluenetics GmbH",
+    0x07C7: "iaconicDesign Inc.",
+    0x07C8: "WRLDS Creations AB",
+    0x07C9: "Skullcandy, Inc.",
+    0x07CA: "Modul-System HH AB",
+    0x07CB: "West Pharmaceutical Services, Inc.",
+    0x07CC: "Barnacle Systems Inc.",
+    0x07CD: "Smart Wave Technologies Canada Inc",
+    0x07CE: "Shanghai Top-Chip Microelectronics Tech. Co., LTD",
+    0x07CF: "NeoSensory, Inc.",
+    0x07D0: "Hangzhou Tuya Information  Technology Co., Ltd",
+    0x07D1: "Shanghai Panchip Microelectronics Co., Ltd",
+    0x07D2: "React Accessibility Limited",
+    0x07D3: "LIVNEX Co.,Ltd.",
+    0x07D4: "Kano Computing Limited",
+    0x07D5: "hoots classic GmbH",
+    0x07D6: "ecobee Inc.",
+    0x07D7: "Nanjing Qinheng Microelectronics Co., Ltd",
+    0x07D8: "SOLUTIONS AMBRA INC.",
+    0x07D9: "Micro-Design, Inc.",
+    0x07DA: "STARLITE Co., Ltd.",
+    0x07DB: "Remedee Labs",
+    0x07DC: "ThingOS GmbH",
+    0x07DD: "Linear Circuits",
+    0x07DE: "Unlimited Engineering SL",
+    0x07DF: "Snap-on Incorporated",
+    0x07E0: "Edifier International Limited",
+    0x07E1: "Lucie Labs",
+    0x07E2: "Alfred Kaercher SE & Co. KG",
+    0x07E3: "Audiowise Technology Inc.",
+    0x07E4: "Geeksme S.L.",
+    0x07E5: "Minut, Inc.",
+    0x07E6: "Autogrow Systems Limited",
+    0x07E7: "Komfort IQ, Inc.",
+    0x07E8: "Packetcraft, Inc.",
+    0x07E9: "Häfele GmbH & Co KG",
+    0x07EA: "ShapeLog, Inc.",
+    0x07EB: "NOVABASE S.R.L.",
+    0x07EC: "Frecce LLC",
+    0x07ED: "Joule IQ, INC.",
+    0x07EE: "KidzTek LLC",
+    0x07EF: "Aktiebolaget Sandvik Coromant",
+    0x07F0: "e-moola.com Pty Ltd",
+    0x07F1: "GSM Innovations Pty Ltd",
+    0x07F2: "SERENE GROUP, INC",
+    0x07F3: "DIGISINE ENERGYTECH CO. LTD.",
+    0x07F4: "MEDIRLAB Orvosbiologiai Fejleszto Korlatolt Felelossegu Tarsasag",
+    0x07F5: "Byton North America Corporation",
+    0x07F6: "Shenzhen TonliScience and Technology Development Co.,Ltd",
+    0x07F7: "Cesar Systems Ltd.",
+    0x07F8: "quip NYC Inc.",
+    0x07F9: "Direct Communication Solutions, Inc.",
+    0x07FA: "Klipsch Group, Inc.",
+    0x07FB: "Access Co., Ltd",
+    0x07FC: "Renault SA",
+    0x07FD: "JSK CO., LTD.",
+    0x07FE: "BIROTA",
+    0x07FF: "maxon motor ltd.",
+    0x0800: "Optek",
+    0x0801: "CRONUS ELECTRONICS LTD",
+    0x0802: "NantSound, Inc.",
+    0x0803: "Domintell s.a.",
+    0x0804: "Andon Health Co.,Ltd",
+    0x0805: "Urbanminded Ltd",
+    0x0806: "TYRI Sweden AB",
+    0x0807: "ECD Electronic Components GmbH Dresden",
+    0x0808: "SISTEMAS KERN, SOCIEDAD ANÓMINA",
+    0x0809: "Trulli Audio",
+    0x080A: "Altaneos",
+    0x080B: "Nanoleaf Canada Limited",
+    0x080C: "Ingy B.V.",
+    0x080D: "Azbil Co.",
+    0x080E: "TATTCOM LLC",
+    0x080F: "Paradox Engineering SA",
+    0x0810: "LECO Corporation",
+    0x0811: "Becker Antriebe GmbH",
+    0x0812: "Mstream Technologies., Inc.",
+    0x0813: "Flextronics International USA Inc.",
+    0x0814: "Ossur hf.",
+    0x0815: "SKC Inc",
+    0x0816: "SPICA SYSTEMS LLC",
+    0x0817: "Wangs Alliance Corporation",
+    0x0818: "tatwah SA",
+    0x0819: "Hunter Douglas Inc",
+    0x081A: "Shenzhen Conex",
+    0x081B: "DIM3",
+    0x081C: "Bobrick Washroom Equipment, Inc.",
+    0x081D: "Potrykus Holdings and Development LLC",
+    0x081E: "iNFORM Technology GmbH",
+    0x081F: "eSenseLab LTD",
+    0x0820: "Brilliant Home Technology, Inc.",
+    0x0821: "INOVA Geophysical, Inc.",
+    0x0822: "adafruit industries",
+    0x0823: "Nexite Ltd",
+    0x0824: "8Power Limited",
+    0x0825: "CME PTE. LTD.",
+    0x0826: "Hyundai Motor Company",
+    0x0827: "Kickmaker",
+    0x0828: "Shanghai Suisheng Information Technology Co., Ltd.",
+    0x0829: "HEXAGON",
+    0x082A: "Mitutoyo Corporation",
+    0x082B: "shenzhen fitcare electronics Co.,Ltd",
+    0x082C: "INGICS TECHNOLOGY CO., LTD.",
+    0x082D: "INCUS PERFORMANCE LTD.",
+    0x082E: "ABB S.p.A.",
+    0x082F: "Blippit AB",
+    0x0830: "Core Health and Fitness LLC",
+    0x0831: "Foxble, LLC",
+    0x0832: "Intermotive,Inc.",
+    0x0833: "Conneqtech B.V.",
+    0x0834: "RIKEN KEIKI CO., LTD.,",
+    0x0835: "Canopy Growth Corporation",
+    0x0836: "Bitwards Oy",
+    0x0837: "vivo Mobile Communication Co., Ltd.",
+    0x0838: "Etymotic Research, Inc.",
+    0x0839: "A puissance 3",
+    0x083A: "BPW Bergische Achsen Kommanditgesellschaft",
+    0x083B: "Piaggio Fast Forward",
+    0x083C: "BeerTech LTD",
+    0x083D: "Tokenize, Inc.",
+    0x083E: "Zorachka LTD",
+    0x083F: "D-Link Corp.",
+    0x0840: "Down Range Systems LLC",
+    0x0841: "General Luminaire (Shanghai) Co., Ltd.",
+    0x0842: "Tangshan HongJia electronic technology co., LTD.",
+    0x0843: "FRAGRANCE DELIVERY TECHNOLOGIES LTD",
+    0x0844: "Pepperl + Fuchs GmbH",
+    0x0845: "Dometic Corporation",
+    0x0846: "USound GmbH",
+    0x0847: "DNANUDGE LIMITED",
+    0x0848: "JUJU JOINTS CANADA CORP.",
+    0x0849: "Dopple Technologies B.V.",
+    0x084A: "ARCOM",
+    0x084B: "Biotechware SRL",
+    0x084C: "ORSO Inc.",
+    0x084D: "SafePort",
+    0x084E: "Carol Cole Company",
+    0x084F: "Embedded Fitness B.V.",
+    0x0850: "Yealink (Xiamen) Network Technology Co.,LTD",
+    0x0851: "Subeca, Inc.",
+    0x0852: "Cognosos, Inc.",
+    0x0853: "Pektron Group Limited",
+    0x0854: "Tap Sound System",
+    0x0855: "Helios Hockey, Inc.",
+    0x0856: "Canopy Growth Corporation",
+    0x0857: "Parsyl Inc",
+    0x0858: "SOUNDBOKS",
+    0x0859: "BlueUp",
+    0x085A: "DAKATECH",
+    0x085B: "RICOH ELECTRONIC DEVICES CO., LTD.",
+    0x085C: "ACOS CO.,LTD.",
+    0x085D: "Guilin Zhishen Information Technology Co.,Ltd.",
+    0x085E: "Krog Systems LLC",
+    0x085F: "COMPEGPS TEAM,SOCIEDAD LIMITADA",
+    0x0860: "Alflex Products B.V.",
+    0x0861: "SmartSensor Labs Ltd",
+    0x0862: "SmartDrive Inc.",
+    0x0863: "Yo-tronics Technology Co., Ltd.",
+    0x0864: "Rafaelmicro",
+    0x0865: "Emergency Lighting Products Limited",
+    0x0866: "LAONZ Co.,Ltd",
+    0x0867: "Western Digital Techologies, Inc.",
+    0x0868: "WIOsense GmbH & Co. KG",
+    0x0869: "EVVA Sicherheitstechnologie GmbH",
+    0x086A: "Odic Incorporated",
+    0x086B: "Pacific Track, LLC",
+    0x086C: "Revvo Technologies, Inc.",
+    0x086D: "Biometrika d.o.o.",
+    0x086E: "Vorwerk Elektrowerke GmbH & Co. KG",
+    0x086F: "Trackunit A/S",
+    0x0870: "Wyze Labs, Inc",
+    0x0871: "Dension Elektronikai Kft. (formerly: Dension Audio Systems Ltd.)",
+    0x0872: "11 Health & Technologies Limited",
+    0x0873: "Innophase Incorporated",
+    0x0874: "Treegreen Limited",
+    0x0875: "Berner International LLC",
+    0x0876: "SmartResQ ApS",
+    0x0877: "Tome, Inc.",
+    0x0878: "The Chamberlain Group, Inc.",
+    0x0879: "MIZUNO Corporation",
+    0x087A: "ZRF, LLC",
+    0x087B: "BYSTAMP",
+    0x087C: "Crosscan GmbH",
+    0x087D: "Konftel AB",
+    0x087E: "1bar.net Limited",
+    0x087F: "Phillips Connect Technologies LLC",
+    0x0880: "imagiLabs AB",
+    0x0881: "Optalert",
+    0x0882: "PSYONIC, Inc.",
+    0x0883: "Wintersteiger AG",
+    0x0884: "Controlid Industria, Comercio de Hardware e Servicos de Tecnologia Ltda",
+    0x0885: "LEVOLOR, INC.",
+    0x0886: "Xsens Technologies B.V.",
+    0x0887: "Hydro-Gear Limited Partnership",
+    0x0888: "EnPointe Fencing Pty Ltd",
+    0x0889: "XANTHIO",
+    0x088A: "sclak s.r.l.",
+    0x088B: "Tricorder Arraay Technologies LLC",
+    0x088C: "GB Solution co.,Ltd",
+    0x088D: "Soliton Systems K.K.",
+    0x088E: "GIGA-TMS INC",
+    0x088F: "Tait International Limited",
+    0x0890: "NICHIEI INTEC CO., LTD.",
+    0x0891: "SmartWireless GmbH & Co. KG",
+    0x0892: "Ingenieurbuero Birnfeld UG (haftungsbeschraenkt)",
+    0x0893: "Maytronics Ltd",
+    0x0894: "EPIFIT",
+    0x0895: "Gimer medical",
+    0x0896: "Nokian Renkaat Oyj",
+    0x0897: "Current Lighting Solutions LLC",
+    0x0898: "Sensibo, Inc.",
+    0x0899: "SFS unimarket AG",
+    0x089A: "Private limited company \"Teltonika\"",
+    0x089B: "Saucon Technologies",
+    0x089C: "Embedded Devices Co. Company",
+    0x089D: "J-J.A.D.E. Enterprise LLC",
+    0x089E: "i-SENS, inc.",
+    0x089F: "Witschi Electronic Ltd",
+    0x08A0: "Aclara Technologies LLC",
+    0x08A1: "EXEO TECH CORPORATION",
+    0x08A2: "Epic Systems Co., Ltd.",
+    0x08A3: "Hoffmann SE",
+    0x08A4: "Realme Chongqing Mobile Telecommunications Corp., Ltd.",
+    0x08A5: "UMEHEAL Ltd",
+    0x08A6: "Intelligenceworks Inc.",
+    0x08A7: "TGR 1.618 Limited",
+    0x08A8: "Shanghai Kfcube Inc",
+    0x08A9: "Fraunhofer IIS",
+    0x08AA: "SZ DJI TECHNOLOGY CO.,LTD",
+    0x08AB: "Coburn Technology, LLC",
+    0x08AC: "Topre Corporation",
+    0x08AD: "Kayamatics Limited",
+    0x08AE: "Moticon ReGo AG",
+    0x08AF: "Polidea Sp. z o.o.",
+    0x08B0: "Trivedi Advanced Technologies LLC",
+    0x08B1: "CORE|vision BV",
+    0x08B2: "PF SCHWEISSTECHNOLOGIE GMBH",
+    0x08B3: "IONIQ Skincare GmbH & Co. KG",
+    0x08B4: "Sengled Co., Ltd.",
+    0x08B5: "TransferFi",
+    0x08B6: "Boehringer Ingelheim Vetmedica GmbH",
+    0x08B7: "ABB Inc",
+    0x08B8: "Check Technology Solutions LLC",
+    0x08B9: "U-Shin Ltd.",
+    0x08BA: "HYPER ICE, INC.",
+    0x08BB: "Tokai-rika co.,ltd.",
+    0x08BC: "Prevayl Limited",
+    0x08BD: "bf1systems limited",
+    0x08BE: "ubisys technologies GmbH",
+    0x08BF: "SIRC Co., Ltd.",
+    0x08C0: "Accent Advanced Systems SLU",
+    0x08C1: "Rayden.Earth LTD",
+    0x08C2: "Lindinvent AB",
+    0x08C3: "CHIPOLO d.o.o.",
+    0x08C4: "CellAssist, LLC",
+    0x08C5: "J. Wagner GmbH",
+    0x08C6: "Integra Optics Inc",
+    0x08C7: "Monadnock Systems Ltd.",
+    0x08C8: "Liteboxer Technologies Inc.",
+    0x08C9: "Noventa AG",
+    0x08CA: "Nubia Technology Co.,Ltd.",
+    0x08CB: "JT INNOVATIONS LIMITED",
+    0x08CC: "TGM TECHNOLOGY CO., LTD.",
+    0x08CD: "ifly",
+    0x08CE: "ZIMI CORPORATION",
+    0x08CF: "betternotstealmybike UG (with limited liability)",
+    0x08D0: "ESTOM Infotech Kft.",
+    0x08D1: "Sensovium Inc.",
+    0x08D2: "Virscient Limited",
+    0x08D3: "Novel Bits, LLC",
+    0x08D4: "ADATA Technology Co., LTD.",
+    0x08D5: "KEYes",
+    0x08D6: "Nome Oy",
+    0x08D7: "Inovonics Corp",
+    0x08D8: "WARES",
+    0x08D9: "Pointr Labs Limited",
+    0x08DA: "Miridia Technology Incorporated",
+    0x08DB: "Tertium Technology",
+    0x08DC: "SHENZHEN AUKEY E BUSINESS CO., LTD",
+    0x08DD: "code-Q",
+    0x08DE: "Tyco Electronics Corporation a TE Connectivity Ltd Company",
+    0x08DF: "IRIS OHYAMA CO.,LTD.",
+    0x08E0: "Philia Technology",
+    0x08E1: "KOZO KEIKAKU ENGINEERING Inc.",
+    0x08E2: "Shenzhen Simo Technology co. LTD",
+    0x08E3: "Republic Wireless, Inc.",
+    0x08E4: "Rashidov ltd",
+    0x08E5: "Crowd Connected Ltd",
+    0x08E6: "Eneso Tecnologia de Adaptacion S.L.",
+    0x08E7: "Barrot Technology Limited",
+    0x08E8: "Naonext",
+    0x08E9: "Taiwan Intelligent Home Corp.",
+    0x08EA: "COWBELL ENGINEERING CO.,LTD.",
+    0x08EB: "Beijing Big Moment Technology Co., Ltd.",
+    0x08EC: "Denso Corporation",
+    0x08ED: "IMI Hydronic Engineering International SA",
+    0x08EE: "ASKEY",
+    0x08EF: "Cumulus Digital Systems, Inc",
+    0x08F0: "Joovv, Inc.",
+    0x08F1: "The L.S. Starrett Company",
+    0x08F2: "Microoled",
+    0x08F3: "PSP - Pauli Services & Products GmbH",
+    0x08F4: "Kodimo Technologies Company Limited",
+    0x08F5: "Tymtix Technologies Private Limited",
+    0x08F6: "Dermal Photonics Corporation",
+    0x08F7: "MTD Products Inc & Affiliates",
+    0x08F8: "instagrid GmbH",
+    0x08F9: "Spacelabs Medical Inc.",
+    0x08FA: "Troo Corporation",
+    0x08FB: "Darkglass Electronics Oy",
+    0x08FC: "Hill-Rom",
+    0x08FD: "BioIntelliSense, Inc.",
+    0x08FE: "Ketronixs Sdn Bhd",
+    0x08FF: "Plastimold Products, Inc",
+    0x0900: "Beijing Zizai Technology Co., LTD.",
+    0x0901: "Lucimed",
+    0x0902: "TSC Auto-ID Technology Co., Ltd.",
+    0x0903: "DATAMARS, Inc.",
+    0x0904: "SUNCORPORATION",
+    0x0905: "Yandex Services AG",
+    0x0906: "Scope Logistical Solutions",
+    0x0907: "User Hello, LLC",
+    0x0908: "Pinpoint Innovations Limited",
+    0x0909: "70mai Co.,Ltd.",
+    0x090A: "Zhuhai Hoksi Technology CO.,LTD",
+    0x090B: "EMBR labs, INC",
+    0x090C: "Radiawave Technologies Co.,Ltd.",
+    0x090D: "IOT Invent GmbH",
+    0x090E: "OPTIMUSIOT TECH LLP",
+    0x090F: "VC Inc.",
+    0x0910: "ASR Microelectronics (Shanghai) Co., Ltd.",
+    0x0911: "Douglas Lighting Controls Inc.",
+    0x0912: "Nerbio Medical Software Platforms Inc",
+    0x0913: "Braveheart Wireless, Inc.",
+    0x0914: "INEO-SENSE",
+    0x0915: "Honda Motor Co., Ltd.",
+    0x0916: "Ambient Sensors LLC",
+    0x0917: "ASR Microelectronics(ShenZhen)Co., Ltd.",
+    0x0918: "Technosphere Labs Pvt. Ltd.",
+    0x0919: "NO SMD LIMITED",
+    0x091A: "Albertronic BV",
+    0x091B: "Luminostics, Inc.",
+    0x091C: "Oblamatik AG",
+    0x091D: "Innokind, Inc.",
+    0x091E: "Melbot Studios, Sociedad Limitada",
+    0x091F: "Myzee Technology",
+    0x0920: "Omnisense Limited",
+    0x0921: "KAHA PTE. LTD.",
+    0x0922: "Shanghai MXCHIP Information Technology Co., Ltd.",
+    0x0923: "JSB TECH PTE LTD",
+    0x0924: "Fundacion Tecnalia Research and Innovation",
+    0x0925: "Yukai Engineering Inc.",
+    0x0926: "Gooligum Technologies Pty Ltd",
+    0x0927: "ROOQ GmbH",
+    0x0928: "AiRISTA",
+    0x0929: "Qingdao Haier Technology Co., Ltd.",
+    0x092A: "Sappl Verwaltungs- und Betriebs GmbH",
+    0x092B: "TekHome",
+    0x092C: "PCI Private Limited",
+    0x092D: "Leggett & Platt, Incorporated",
+    0x092E: "PS GmbH",
+    0x092F: "C.O.B.O. SpA",
+    0x0930: "James Walker RotaBolt Limited",
+    0x0931: "BREATHINGS Co., Ltd.",
+    0x0932: "BarVision, LLC",
+    0x0933: "SRAM",
+    0x0934: "KiteSpring Inc.",
+    0x0935: "Reconnect, Inc.",
+    0x0936: "Elekon AG",
+    0x0937: "RealThingks GmbH",
+    0x0938: "Henway Technologies, LTD.",
+    0x0939: "ASTEM Co.,Ltd.",
+    0x093A: "LinkedSemi Microelectronics (Xiamen) Co., Ltd",
+    0x093B: "ENSESO LLC",
+    0x093C: "Xenoma Inc.",
+    0x093D: "Adolf Wuerth GmbH & Co KG",
+    0x093E: "Catalyft Labs, Inc.",
+    0x093F: "JEPICO Corporation",
+    0x0940: "Hero Workout GmbH",
+    0x0941: "Rivian Automotive, LLC",
+    0x0942: "TRANSSION HOLDINGS LIMITED",
+    0x0943: "Inovonics Corp.",
+    0x0944: "Agitron d.o.o.",
+    0x0945: "Globe (Jiangsu) Co., Ltd",
+    0x0946: "AMC International Alfa Metalcraft Corporation AG",
+    0x0947: "First Light Technologies Ltd.",
+    0x0948: "Wearable Link Limited",
+    0x0949: "Metronom Health Europe",
+    0x094A: "Zwift, Inc.",
+    0x094B: "Kindeva Drug Delivery L.P.",
+    0x094C: "GimmiSys GmbH",
+    0x094D: "tkLABS INC.",
+    0x094E: "PassiveBolt, Inc.",
+    0x094F: "Limited Liability Company \"Mikrotikls\"",
+    0x0950: "Capetech",
+    0x0951: "PPRS",
+    0x0952: "Apptricity Corporation",
+    0x0953: "LogiLube, LLC",
+    0x0954: "Julbo",
+    0x0955: "Breville Group",
+    0x0956: "Kerlink",
+    0x0957: "Ohsung Electronics",
+    0x0958: "ZTE Corporation",
+    0x0959: "HerdDogg, Inc",
+    0x095A: "Selekt Bilgisayar, lletisim Urunleri lnsaat Sanayi ve Ticaret Limited Sirketi",
+    0x095B: "Lismore Instruments Limited",
+    0x095C: "LogiLube, LLC",
+    0x095D: "ETC",
+    0x095E: "BioEchoNet inc.",
+    0x095F: "NUANCE HEARING LTD",
+    0x0960: "Sena Technologies Inc.",
+    0x0961: "Linkura AB",
+    0x0962: "GL Solutions K.K.",
+    0x0963: "Moonbird BV",
+    0x0964: "Countrymate Technology Limited",
+    0x0965: "Asahi Kasei Corporation",
+    0x0966: "PointGuard, LLC",
+    0x0967: "Neo Materials and Consulting Inc.",
+    0x0968: "Actev Motors, Inc.",
+    0x0969: "Woan Technology (Shenzhen) Co., Ltd.",
+    0x096A: "dricos, Inc.",
+    0x096B: "Guide ID B.V.",
+    0x096C: "9374-7319 Quebec inc",
+    0x096D: "Gunwerks, LLC",
+    0x096E: "Band Industries, inc.",
+    0x096F: "Lund Motion Products, Inc.",
+    0x0970: "IBA Dosimetry GmbH",
+    0x0971: "GA",
+    0x0972: "Closed Joint Stock Company \"Zavod Flometr\" (\"Zavod Flometr\" CJSC)",
+    0x0973: "Popit Oy",
+    0x0974: "ABEYE",
+    0x0975: "BlueIOT(Beijing) Technology Co.,Ltd",
+    0x0976: "Fauna Audio GmbH",
+    0x0977: "TOYOTA motor corporation",
+    0x0978: "ZifferEins GmbH & Co. KG",
+    0x0979: "BIOTRONIK SE & Co. KG",
+    0x097A: "CORE CORPORATION",
+    0x097B: "CTEK Sweden AB",
+    0x097C: "Thorley Industries, LLC",
+    0x097D: "CLB B.V.",
+    0x097E: "SonicSensory Inc",
+    0x097F: "ISEMAR S.R.L.",
+    0x0980: "DEKRA TESTING AND CERTIFICATION, S.A.U.",
+    0x0981: "Bernard Krone Holding SE & Co.KG",
+    0x0982: "ELPRO-BUCHS AG",
+    0x0983: "Feedback Sports LLC",
+    0x0984: "TeraTron GmbH",
+    0x0985: "Lumos Health Inc.",
+    0x0986: "Cello Hill, LLC",
+    0x0987: "TSE BRAKES, INC.",
+    0x0988: "BHM-Tech Produktionsgesellschaft m.b.H",
+    0x0989: "WIKA Alexander Wiegand SE & Co.KG",
+    0x098A: "Biovigil",
+    0x098B: "Mequonic Engineering, S.L.",
+    0x098C: "bGrid B.V.",
+    0x098D: "C3-WIRELESS, LLC",
+    0x098E: "ADVEEZ",
+    0x098F: "Aktiebolaget Regin",
+    0x0990: "Anton Paar GmbH",
+    0x0991: "Telenor ASA",
+    0x0992: "Big Kaiser Precision Tooling Ltd",
+    0x0993: "Absolute Audio Labs B.V.",
+    0x0994: "VT42 Pty Ltd",
+    0x0995: "Bronkhorst High-Tech B.V.",
+    0x0996: "C. & E. Fein GmbH",
+    0x0997: "NextMind",
+    0x0998: "Pixie Dust Technologies, Inc.",
+    0x0999: "eTactica ehf",
+    0x099A: "New Audio LLC",
+    0x099B: "Sendum Wireless Corporation",
+    0x099C: "deister electronic GmbH",
+    0x099D: "YKK AP Inc.",
+    0x099E: "Step One Limited",
+    0x099F: "Koya Medical, Inc.",
+    0x09A0: "Proof Diagnostics, Inc.",
+    0x09A1: "VOS Systems, LLC",
+    0x09A2: "ENGAGENOW DATA SCIENCES PRIVATE LIMITED",
+    0x09A3: "ARDUINO SA",
+    0x09A4: "KUMHO ELECTRICS, INC",
+    0x09A5: "Security Enhancement Systems, LLC",
+    0x09A6: "BEIJING ELECTRIC VEHICLE CO.,LTD",
+    0x09A7: "Paybuddy ApS",
+    0x09A8: "KHN Solutions Inc",
+    0x09A9: "Nippon Ceramic Co.,Ltd.",
+    0x09AA: "PHOTODYNAMIC INCORPORATED",
+    0x09AB: "DashLogic, Inc.",
+    0x09AC: "Ambiq",
+    0x09AD: "Narhwall Inc.",
+    0x09AE: "Pozyx NV",
+    0x09AF: "ifLink Open Community",
+    0x09B0: "Deublin Company, LLC",
+    0x09B1: "BLINQY",
+    0x09B2: "DYPHI",
+    0x09B3: "BlueX Microelectronics Corp Ltd.",
+    0x09B4: "PentaLock Aps.",
+    0x09B5: "AUTEC Gesellschaft fuer Automationstechnik mbH",
+    0x09B6: "Pegasus Technologies, Inc.",
+    0x09B7: "Bout Labs, LLC",
+    0x09B8: "PlayerData Limited",
+    0x09B9: "SAVOY ELECTRONIC LIGHTING",
+    0x09BA: "Elimo Engineering Ltd",
+    0x09BB: "SkyStream Corporation",
+    0x09BC: "Aerosens LLC",
+    0x09BD: "Centre Suisse d'Electronique et de Microtechnique SA",
+    0x09BE: "Vessel Ltd.",
+    0x09BF: "Span.IO, Inc.",
+    0x09C0: "AnotherBrain inc.",
+    0x09C1: "Rosewill",
+    0x09C2: "Universal Audio, Inc.",
+    0x09C3: "JAPAN TOBACCO INC.",
+    0x09C4: "UVISIO",
+    0x09C5: "HungYi Microelectronics Co.,Ltd.",
+    0x09C6: "Honor Device Co., Ltd.",
+    0x09C7: "Combustion, LLC",
+    0x09C8: "XUNTONG",
+    0x09C9: "CrowdGlow Ltd",
+    0x09CA: "Mobitrace",
+    0x09CB: "Hx Engineering, LLC",
+    0x09CC: "Senso4s d.o.o.",
+    0x09CD: "Blyott",
+    0x09CE: "Julius Blum GmbH",
+    0x09CF: "BlueStreak IoT, LLC",
+    0x09D0: "Chess Wise B.V.",
+    0x09D1: "ABLEPAY TECHNOLOGIES AS",
+    0x09D2: "Temperature Sensitive Solutions Systems Sweden AB",
+    0x09D3: "HeartHero, inc.",
+    0x09D4: "ORBIS Inc.",
+    0x09D5: "GEAR RADIO ELECTRONICS CORP.",
+    0x09D6: "EAR TEKNIK ISITME VE ODIOMETRI CIHAZLARI SANAYI VE TICARET ANONIM SIRKETI",
+    0x09D7: "Coyotta",
+    0x09D8: "Synergy Tecnologia em Sistemas Ltda",
+    0x09D9: "VivoSensMedical GmbH",
+    0x09DA: "Nagravision SA",
+    0x09DB: "Bionic Avionics Inc.",
+    0x09DC: "AON2 Ltd.",
+    0x09DD: "Innoware Development AB",
+    0x09DE: "JLD Technology Solutions, LLC",
+    0x09DF: "Magnus Technology Sdn Bhd",
+    0x09E0: "Preddio Technologies Inc.",
+    0x09E1: "Tag-N-Trac Inc",
+    0x09E2: "Wuhan Linptech Co.,Ltd.",
+    0x09E3: "Friday Home Aps",
+    0x09E4: "CPS AS",
+    0x09E5: "Mobilogix",
+    0x09E6: "Masonite Corporation",
+    0x09E7: "Kabushikigaisha HANERON",
+    0x09E8: "Melange Systems Pvt. Ltd.",
+    0x09E9: "LumenRadio AB",
+    0x09EA: "Athlos Oy",
+    0x09EB: "KEAN ELECTRONICS PTY LTD",
+    0x09EC: "Yukon advanced optics worldwide, UAB",
+    0x09ED: "Sibel Inc.",
+    0x09EE: "OJMAR SA",
+    0x09EF: "Steinel Solutions AG",
+    0x09F0: "WatchGas B.V.",
+    0x09F1: "OM Digital Solutions Corporation",
+    0x09F2: "Audeara Pty Ltd",
+    0x09F3: "Beijing Zero Zero Infinity Technology Co.,Ltd.",
+    0x09F4: "Spectrum Technologies, Inc.",
+    0x09F5: "OKI Electric Industry Co., Ltd",
+    0x09F6: "Mobile Action Technology Inc.",
+    0x09F7: "SENSATEC Co., Ltd.",
+    0x09F8: "R.O. S.R.L.",
+    0x09F9: "Hangzhou Yaguan Technology Co. LTD",
+    0x09FA: "Listen Technologies Corporation",
+    0x09FB: "TOITU CO., LTD.",
+    0x09FC: "Confidex",
+    0x09FD: "Keep Technologies, Inc.",
+    0x09FE: "Lichtvision Engineering GmbH",
+    0x09FF: "AIRSTAR",
+    0x0A00: "Ampler Bikes OU",
+    0x0A01: "Cleveron AS",
+    0x0A02: "Ayxon-Dynamics GmbH",
+    0x0A03: "donutrobotics Co., Ltd.",
+    0x0A04: "Flosonics Medical",
+    0x0A05: "Southwire Company, LLC",
+    0x0A06: "Shanghai wuqi microelectronics Co.,Ltd",
+    0x0A07: "Reflow Pty Ltd",
+    0x0A08: "Oras Oy",
+    0x0A09: "ECCT",
+    0x0A0A: "Volan Technology Inc.",
+    0x0A0B: "SIANA Systems",
+    0x0A0C: "Shanghai Yidian Intelligent Technology Co., Ltd.",
+    0x0A0D: "Blue Peacock GmbH",
+    0x0A0E: "Roland Corporation",
+    0x0A0F: "LIXIL Corporation",
+    0x0A10: "SUBARU Corporation",
+    0x0A11: "Sensolus",
+    0x0A12: "Dyson Technology Limited",
+    0x0A13: "Tec4med LifeScience GmbH",
+    0x0A14: "CROXEL, INC.",
+    0x0A15: "Syng Inc",
+    0x0A16: "RIDE VISION LTD",
+    0x0A17: "Plume Design Inc",
+    0x0A18: "Cambridge Animal Technologies Ltd",
+    0x0A19: "Maxell, Ltd.",
+    0x0A1A: "Link Labs, Inc.",
+    0x0A1B: "Embrava Pty Ltd",
+    0x0A1C: "INPEAK S.C.",
+    0x0A1D: "API-K",
+    0x0A1E: "CombiQ AB",
+    0x0A1F: "DeVilbiss Healthcare LLC",
+    0x0A20: "Jiangxi Innotech Technology Co., Ltd",
+    0x0A21: "Apollogic Sp. z o.o.",
+    0x0A22: "DAIICHIKOSHO CO., LTD.",
+    0x0A23: "BIXOLON CO.,LTD",
+    0x0A24: "Atmosic Technologies, Inc.",
+    0x0A25: "Eran Financial Services LLC",
+    0x0A26: "Louis Vuitton",
+    0x0A27: "AYU DEVICES PRIVATE LIMITED",
+    0x0A28: "NanoFlex",
+    0x0A29: "Worthcloud Technology Co.,Ltd",
+    0x0A2A: "Yamaha Corporation",
+    0x0A2B: "PaceBait IVS",
+    0x0A2C: "Shenzhen H&T Intelligent Control Co., Ltd",
+    0x0A2D: "Shenzhen Feasycom Technology Co., Ltd.",
+    0x0A2E: "Zuma Array Limited",
+    0x0A2F: "Instamic, Inc.",
+    0x0A30: "Air-Weigh",
+    0x0A31: "Nevro Corp.",
+    0x0A32: "Pinnacle Technology, Inc.",
+    0x0A33: "WMF AG",
+    0x0A34: "Luxer Corporation",
+    0x0A35: "safectory GmbH",
+    0x0A36: "NGK SPARK PLUG CO., LTD.",
+    0x0A37: "2587702 Ontario Inc.",
+    0x0A38: "Bouffalo Lab (Nanjing)., Ltd.",
+    0x0A39: "BLUETICKETING SRL",
+    0x0A3A: "Incotex Co. Ltd.",
+    0x0A3B: "Galileo Technology Limited",
+    0x0A3C: "Siteco GmbH",
+    0x0A3D: "DELABIE",
+    0x0A3E: "Hefei Yunlian Semiconductor Co., Ltd",
+    0x0A3F: "Shenzhen Yopeak Optoelectronics Technology Co., Ltd.",
+    0x0A40: "GEWISS S.p.A.",
+    0x0A41: "OPEX Corporation",
+    0x0A42: "Motionalysis, Inc.",
+    0x0A43: "Busch Systems International Inc.",
+    0x0A44: "Novidan, Inc.",
+    0x0A45: "3SI Security Systems, Inc",
+    0x0A46: "Beijing HC-Infinite Technology Limited",
+    0x0A47: "The Wand Company Ltd",
+    0x0A48: "JRC Mobility Inc.",
+    0x0A49: "Venture Research Inc.",
+    0x0A4A: "Map Large, Inc.",
+    0x0A4B: "MistyWest Energy and Transport Ltd.",
+    0x0A4C: "SiFli Technologies (shanghai) Inc.",
+    0x0A4D: "Lockn Technologies Private Limited",
+    0x0A4E: "Toytec Corporation",
+    0x0A4F: "VANMOOF Global Holding B.V.",
+    0x0A50: "Nextscape Inc.",
+    0x0A51: "CSIRO",
+    0x0A52: "Follow Sense Europe B.V.",
+    0x0A53: "KKM COMPANY LIMITED",
+    0x0A54: "SQL Technologies Corp.",
+    0x0A55: "Inugo Systems Limited",
+    0x0A56: "ambie",
+    0x0A57: "Meizhou Guo Wei Electronics Co., Ltd",
+    0x0A58: "Indigo Diabetes",
+    0x0A59: "TourBuilt, LLC",
+    0x0A5A: "Sontheim Industrie Elektronik GmbH",
+    0x0A5B: "LEGIC Identsystems AG",
+    0x0A5C: "Innovative Design Labs Inc.",
+    0x0A5D: "MG Energy Systems B.V.",
+    0x0A5E: "LaceClips llc",
+    0x0A5F: "stryker",
+    0x0A60: "DATANG SEMICONDUCTOR TECHNOLOGY CO.,LTD",
+    0x0A61: "Smart Parks B.V.",
+    0x0A62: "MOKO TECHNOLOGY Ltd",
+    0x0A63: "Gremsy JSC",
+    0x0A64: "Geopal system A/S",
+    0x0A65: "Lytx, INC.",
+    0x0A66: "JUSTMORPH PTE. LTD.",
+    0x0A67: "Beijing SuperHexa Century Technology CO. Ltd",
+    0x0A68: "Focus Ingenieria SRL",
+    0x0A69: "HAPPIEST BABY, INC.",
+    0x0A6A: "Scribble Design Inc.",
+    0x0A6B: "Olympic Ophthalmics, Inc.",
+    0x0A6C: "Pokkels",
+    0x0A6D: "KUUKANJYOKIN Co.,Ltd.",
+    0x0A6E: "Pac Sane Limited",
+    0x0A6F: "Warner Bros.",
+    0x0A70: "Ooma",
+    0x0A71: "Senquip Pty Ltd",
+    0x0A72: "Jumo GmbH & Co. KG",
+    0x0A73: "Innohome Oy",
+    0x0A74: "MICROSON S.A.",
+    0x0A75: "Delta Cycle Corporation",
+    0x0A76: "Synaptics Incorporated",
+    0x0A77: "JMD PACIFIC PTE. LTD.",
+    0x0A78: "Shenzhen Sunricher Technology Limited",
+    0x0A79: "Webasto SE",
+    0x0A7A: "Emlid Limited",
+    0x0A7B: "UniqAir Oy",
+    0x0A7C: "WAFERLOCK",
+    0x0A7D: "Freedman Electronics Pty Ltd",
+    0x0A7E: "Keba AG",
+    0x0A7F: "Intuity Medical"
+}
\ No newline at end of file
diff --git a/bumble/controller.py b/bumble/controller.py
new file mode 100644
index 0000000..35f7e92
--- /dev/null
+++ b/bumble/controller.py
@@ -0,0 +1,895 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import itertools
+import random
+
+from .hci import *
+from .l2cap import *
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+class DataObject:
+    pass
+
+
+# -----------------------------------------------------------------------------
+class Connection:
+    def __init__(self, controller, handle, role, peer_address, link):
+        self.controller   = controller
+        self.handle       = handle
+        self.role         = role
+        self.peer_address = peer_address
+        self.link         = link
+        self.assembler    = HCI_AclDataPacketAssembler(self.on_acl_pdu)
+
+    def on_hci_acl_data_packet(self, packet):
+        self.assembler.feed_packet(packet)
+        self.controller.send_hci_packet(HCI_Number_Of_Completed_Packets_Event([(self.handle, 1)]))
+
+    def on_acl_pdu(self, data):
+        if self.link:
+            self.link.send_acl_data(self.controller.random_address, self.peer_address, data)
+
+
+# -----------------------------------------------------------------------------
+class Controller:
+    def __init__(self, name, host_source = None, host_sink = None, link = None):
+        self.name     = name
+        self.hci_sink = None
+        self.link     = link
+
+        self.central_connections    = {}  # Connections where this controller is the central
+        self.peripheral_connections = {}  # Connections where this controller is the peripheral
+
+        self.hci_version                  = HCI_VERSION_BLUETOOTH_CORE_5_0
+        self.hci_revision                 = 0
+        self.lmp_version                  = HCI_VERSION_BLUETOOTH_CORE_5_0
+        self.lmp_subversion               = 0
+        self.lmp_features                 = bytes.fromhex('0000000060000000')  # BR/EDR Not Supported, LE Supported (Controller)
+        self.manufacturer_name            = 0xFFFF
+        self.hc_le_data_packet_length     = 27
+        self.hc_total_num_le_data_packets = 64
+        self.supported_commands           = bytes.fromhex('2000800000c000000000e40000002822000000000000040000f7ffff7f00000030f0f9ff01008004000000000000000000000000000000000000000000000000')
+        self.le_features                  = bytes.fromhex('ff49010000000000')
+        self.le_states                    = bytes.fromhex('ffff3fffff030000')
+        self.avertising_channel_tx_power  = 0
+        self.white_list_size              = 8
+        self.resolving_list_size          = 8
+        self.supported_max_tx_octets      = 27
+        self.supported_max_tx_time        = 10000  # microseconds
+        self.supported_max_rx_octets      = 27
+        self.supported_max_rx_time        = 10000  # microseconds
+        self.suggested_max_tx_octets      = 27
+        self.suggested_max_tx_time        = 0x0148  # microseconds
+        self.default_phy                  = bytes([0, 0, 0])
+        self.le_scan_type                 = 0
+        self.le_scan_interval             = 0x10
+        self.le_scan_window               = 0x10
+        self.le_scan_enable               = 0
+        self.le_scan_own_address_type     = Address.RANDOM_DEVICE_ADDRESS
+        self.le_scanning_filter_policy    = 0
+        self.le_scan_response_data        = None
+        self.le_address_resolution        = False
+        self.le_rpa_timeout               = 0
+        self.sync_flow_control            = False
+        self.local_name                   = 'Bumble'
+
+        self.advertising_interval     = 2000  # Fixed for now
+        self.advertising_data         = None
+        self.advertising_timer_handle = None
+
+        self._random_address = Address('00:00:00:00:00:00')
+        self._public_address = None
+
+        # Set the source and sink interfaces
+        if host_source:
+            host_source.set_packet_sink(self)
+        self.host = host_sink
+
+        # Add this controller to the link if specified
+        if link:
+            link.add_controller(self)
+
+    @property
+    def host(self):
+        return self.hci_sink
+
+    @host.setter
+    def host(self, host):
+        '''
+        Sets the host (sink) for this controller, and set this controller as the controller (sink) for the host
+        '''
+        self.set_packet_sink(host)
+        if host:
+            host.controller = self
+
+    def set_packet_sink(self, sink):
+        '''
+        Method from the Packet Source interface
+        '''
+        self.hci_sink = sink
+
+    @property
+    def public_address(self):
+        return self._public_address
+
+    @public_address.setter
+    def public_address(self, address):
+        if type(address) is str:
+            address = Address(address)
+        self._public_address = address
+
+    @property
+    def random_address(self):
+        return self._random_address
+
+    @random_address.setter
+    def random_address(self, address):
+        if type(address) is str:
+            address = Address(address)
+        self._random_address = address
+        logger.debug(f'new random address: {address}')
+
+        if self.link:
+            self.link.on_address_changed(self)
+
+    # Packet Sink protocol (packets coming from the host via HCI)
+    def on_packet(self, packet):
+        self.on_hci_packet(HCI_Packet.from_bytes(packet))
+
+    def on_hci_packet(self, packet):
+        logger.debug(f'{color("<<<", "blue")} [{self.name}] {color("HOST -> CONTROLLER", "blue")}: {packet}')
+
+        # If the packet is a command, invoke the handler for this packet
+        if packet.hci_packet_type == HCI_COMMAND_PACKET:
+            self.on_hci_command_packet(packet)
+        elif packet.hci_packet_type == HCI_EVENT_PACKET:
+            self.on_hci_event_packet(packet)
+        elif packet.hci_packet_type == HCI_ACL_DATA_PACKET:
+            self.on_hci_acl_data_packet(packet)
+        else:
+            logger.warning(f'!!! unknown packet type {packet.hci_packet_type}')
+
+    def on_hci_command_packet(self, command):
+        handler_name = f'on_{command.name.lower()}'
+        handler = getattr(self, handler_name, self.on_hci_command)
+        result = handler(command)
+        if type(result) is bytes:
+            self.send_hci_packet(HCI_Command_Complete_Event(
+                num_hci_command_packets = 1,
+                command_opcode          = command.op_code,
+                return_parameters       = result
+            ))
+
+    def on_hci_event_packet(self, event):
+        logger.warning('!!! unexpected event packet')
+
+    def on_hci_acl_data_packet(self, packet):
+        # Look for the connection to which this data belongs
+        connection = self.find_connection_by_handle(packet.connection_handle)
+        if connection is None:
+            logger.warning(f'!!! no connection for handle 0x{packet.connection_handle:04X}')
+            return
+
+        # Pass the packet to the connection
+        connection.on_hci_acl_data_packet(packet)
+
+    def send_hci_packet(self, packet):
+        logger.debug(f'{color(">>>", "green")} [{self.name}] {color("CONTROLLER -> HOST", "green")}: {packet}')
+        if self.host:
+            self.host.on_packet(packet.to_bytes())
+
+    # This method allow the controller to emulate the same API as a transport source
+    async def wait_for_termination(self):
+        # For now, just wait forever
+        await asyncio.get_running_loop().create_future()
+
+    ############################################################
+    # Link connections
+    ############################################################
+    def allocate_connection_handle(self):
+        handle = 0
+        max_handle = 0
+        for connection in itertools.chain(
+            self.central_connections.values(),
+            self.peripheral_connections.values()
+        ):
+            max_handle = max(max_handle, connection.handle)
+            if connection.handle == handle:
+                # Already used, continue searching after the current max
+                handle = max_handle + 1
+        return handle
+
+    def find_connection_by_address(self, address):
+        return self.central_connections.get(address) or self.peripheral_connections.get(address)
+
+    def find_connection_by_handle(self, handle):
+        for connection in itertools.chain(
+            self.central_connections.values(),
+            self.peripheral_connections.values()
+        ):
+            if connection.handle == handle:
+                return connection
+        return None
+
+    def find_central_connection_by_handle(self, handle):
+        for connection in self.central_connections.values():
+            if connection.handle == handle:
+                return connection
+        return None
+
+    def on_link_central_connected(self, central_address):
+        '''
+        Called when an incoming connection occurs from a central on the link
+        '''
+
+        # Allocate (or reuse) a connection handle
+        peer_address = central_address
+        peer_address_type = central_address.address_type
+        connection = self.peripheral_connections.get(peer_address)
+        if connection is None:
+            connection_handle = self.allocate_connection_handle()
+            connection = Connection(self, connection_handle, BT_PERIPHERAL_ROLE, peer_address, self.link)
+            self.peripheral_connections[peer_address] = connection
+            logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}')
+
+        # Then say that the connection has completed
+        self.send_hci_packet(HCI_LE_Connection_Complete_Event(
+            status                = HCI_SUCCESS,
+            connection_handle     = connection.handle,
+            role                  = connection.role,
+            peer_address_type     = peer_address_type,
+            peer_address          = peer_address,
+            conn_interval         = 10,  # FIXME
+            conn_latency          = 0,   # FIXME
+            supervision_timeout   = 10,  # FIXME
+            master_clock_accuracy = 7    # FIXME
+        ))
+
+    def on_link_central_disconnected(self, peer_address, reason):
+        '''
+        Called when an active disconnection occurs from a peer
+        '''
+
+        # Send a disconnection complete event
+        if connection := self.peripheral_connections.get(peer_address):
+            self.send_hci_packet(HCI_Disconnection_Complete_Event(
+                status            = HCI_SUCCESS,
+                connection_handle = connection.handle,
+                reason            = reason
+            ))
+
+            # Remove the connection
+            del self.peripheral_connections[peer_address]
+        else:
+            logger.warn(f'!!! No peripheral connection found for {peer_address}')
+
+    def on_link_peripheral_connection_complete(self, le_create_connection_command, status):
+        '''
+        Called by the link when a connection has been made or has failed to be made
+        '''
+
+        if status == HCI_SUCCESS:
+            # Allocate (or reuse) a connection handle
+            peer_address = le_create_connection_command.peer_address
+            connection = self.central_connections.get(peer_address)
+            if connection is None:
+                connection_handle = self.allocate_connection_handle()
+                connection = Connection(
+                    self,
+                    connection_handle,
+                    BT_CENTRAL_ROLE,
+                    peer_address,
+                    self.link
+                )
+                self.central_connections[peer_address] = connection
+                logger.debug(f'New CENTRAL connection handle: 0x{connection_handle:04X}')
+        else:
+            connection = None
+
+        # Say that the connection has completed
+        self.send_hci_packet(HCI_LE_Connection_Complete_Event(
+            status                = status,
+            connection_handle     = connection.handle if connection else 0,
+            role                  = BT_CENTRAL_ROLE,
+            peer_address_type     = le_create_connection_command.peer_address_type,
+            peer_address          = le_create_connection_command.peer_address,
+            conn_interval         = le_create_connection_command.conn_interval_min,
+            conn_latency          = le_create_connection_command.conn_latency,
+            supervision_timeout   = le_create_connection_command.supervision_timeout,
+            master_clock_accuracy = 0
+        ))
+
+    def on_link_peripheral_disconnection_complete(self, disconnection_command, status):
+        '''
+        Called when a disconnection has been completed
+        '''
+
+        # Send a disconnection complete event
+        self.send_hci_packet(HCI_Disconnection_Complete_Event(
+            status            = status,
+            connection_handle = disconnection_command.connection_handle,
+            reason            = disconnection_command.reason
+        ))
+
+        # Remove the connection
+        if connection := self.find_central_connection_by_handle(disconnection_command.connection_handle):
+            logger.debug(f'CENTRAL Connection removed: {connection}')
+            del self.central_connections[connection.peer_address]
+
+    def on_link_peripheral_disconnected(self, peer_address):
+        '''
+        Called when a connection to a peripheral is broken
+        '''
+
+        # Send a disconnection complete event
+        if connection := self.central_connections.get(peer_address):
+            self.send_hci_packet(HCI_Disconnection_Complete_Event(
+                status            = HCI_SUCCESS,
+                connection_handle = connection.handle,
+                reason            = HCI_CONNECTION_TIMEOUT_ERROR
+            ))
+
+            # Remove the connection
+            del self.central_connections[peer_address]
+        else:
+            logger.warn(f'!!! No central connection found for {peer_address}')
+
+    def on_link_encrypted(self, peer_address, rand, ediv, ltk):
+        # For now, just setup the encryption without asking the host
+        if connection := self.find_connection_by_address(peer_address):
+            self.send_hci_packet(
+                HCI_Encryption_Change_Event(
+                    status             = 0,
+                    connection_handle  = connection.handle,
+                    encryption_enabled = 1
+                )
+            )
+
+    def on_link_acl_data(self, sender_address, data):
+        # Look for the connection to which this data belongs
+        connection = self.find_connection_by_address(sender_address)
+        if connection is None:
+            logger.warning(f'!!! no connection for {sender_address}')
+            return
+
+        # Send the data to the host
+        # TODO: should fragment
+        acl_packet = HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
+        self.send_hci_packet(acl_packet)
+
+    def on_link_advertising_data(self, sender_address, data):
+        # Ignore if we're not scanning
+        if self.le_scan_enable == 0:
+            return
+
+        # Send a scan report
+        report = HCI_Object(
+            HCI_LE_Advertising_Report_Event.REPORT_FIELDS,
+            event_type   = HCI_LE_Advertising_Report_Event.ADV_IND,
+            address_type = sender_address.address_type,
+            address      = sender_address,
+            data         = data,
+            rssi         = -50
+        )
+        self.send_hci_packet(HCI_LE_Advertising_Report_Event([report]))
+
+        # Simulate a scan response
+        report = HCI_Object(
+            HCI_LE_Advertising_Report_Event.REPORT_FIELDS,
+            event_type   = HCI_LE_Advertising_Report_Event.SCAN_RSP,
+            address_type = sender_address.address_type,
+            address      = sender_address,
+            data         = data,
+            rssi         = -50
+        )
+        self.send_hci_packet(HCI_LE_Advertising_Report_Event([report]))
+
+    ############################################################
+    # Advertising support
+    ############################################################
+    def on_advertising_timer_fired(self):
+        self.send_advertising_data()
+        self.advertising_timer_handle = asyncio.get_running_loop().call_later(self.advertising_interval / 1000.0, self.on_advertising_timer_fired)
+
+    def start_advertising(self):
+        # Stop any ongoing advertising before we start again
+        self.stop_advertising()
+
+        # Advertise now
+        self.advertising_timer_handle = asyncio.get_running_loop().call_soon(self.on_advertising_timer_fired)
+
+    def stop_advertising(self):
+        if self.advertising_timer_handle is not None:
+            self.advertising_timer_handle.cancel()
+            self.advertising_timer_handle = None
+
+    def send_advertising_data(self):
+        if self.link and self.advertising_data:
+            self.link.send_advertising_data(self.random_address, self.advertising_data)
+
+    @property
+    def is_advertising(self):
+        return self.advertising_timer_handle is not None
+
+    ############################################################
+    # HCI handlers
+    ############################################################
+    def on_hci_command(self, command):
+        logger.warning(color(f'--- Unsupported command {command}', 'red'))
+        return bytes([HCI_UNKNOWN_HCI_COMMAND_ERROR])
+
+    def on_hci_create_connection_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.1.5 Create Connection command
+        '''
+
+        # TODO: classic mode not supported yet
+
+    def on_hci_disconnect_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.1.6 Disconnect Command
+        '''
+        # First, say that the disconnection is pending
+        self.send_hci_packet(HCI_Command_Status_Event(
+            status                  = HCI_COMMAND_STATUS_PENDING,
+            num_hci_command_packets = 1,
+            command_opcode          = command.op_code
+        ))
+
+        # Notify the link of the disconnection
+        if not (connection := self.find_central_connection_by_handle(command.connection_handle)):
+            logger.warn('connection not found')
+            return
+
+        if self.link:
+            self.link.disconnect(self.random_address, connection.peer_address, command)
+        else:
+            # Remove the connection
+            del self.central_connections[connection.peer_address]
+
+    def on_hci_set_event_mask_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.1 Set Event Mask Command
+        '''
+        self.event_mask = command.event_mask
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_reset_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.2 Reset Command
+        '''
+        # TODO: cleanup what needs to be reset
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_write_local_name_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.11 Write Local Name Command
+        '''
+        local_name = command.local_name
+        if len(local_name):
+            try:
+                first_null = local_name.find(0)
+                if first_null >= 0:
+                    local_name = local_name[:first_null]
+                self.local_name = str(local_name, 'utf-8')
+            except UnicodeDecodeError:
+                pass
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_read_local_name_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.12 Read Local Name Command
+        '''
+        local_name = bytes(self.local_name, 'utf-8')[:248]
+        if len(local_name) < 248:
+            local_name = local_name + bytes(248 - len(local_name))
+
+        return bytes([HCI_SUCCESS]) + local_name
+
+    def on_hci_read_class_of_device_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.25 Read Class of Device Command
+        '''
+        return bytes([HCI_SUCCESS, 0, 0, 0])
+
+    def on_hci_write_class_of_device_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.26 Write Class of Device Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_read_synchronous_flow_control_enable_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.36 Read Synchronous Flow Control Enable Command
+        '''
+        if self.sync_flow_control:
+            ret = 1
+        else:
+            ret = 0
+        return bytes([HCI_SUCCESS, ret])
+
+    def on_hci_write_synchronous_flow_control_enable_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.37 Write Synchronous Flow Control Enable Command
+        '''
+        ret = HCI_SUCCESS
+        if command.synchronous_flow_control_enable == 1:
+            self.sync_flow_control = True
+        elif command.synchronous_flow_control_enable == 0:
+            self.sync_flow_control = False
+        else:
+            ret = HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR
+        return bytes([ret])
+
+    def on_hci_write_simple_pairing_mode_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.59 Write Simple Pairing Mode Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_set_event_mask_page_2_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.69 Set Event Mask Page 2 Command
+        '''
+        self.event_mask_page_2 = command.event_mask_page_2
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_read_le_host_support_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.78 Write LE Host Support Command
+        '''
+        return bytes([HCI_SUCCESS, 1, 0])
+
+    def on_hci_write_le_host_support_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.79 Write LE Host Support Command
+        '''
+        # TODO / Just ignore for now
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_write_authenticated_payload_timeout_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.3.94 Write Authenticated Payload Timeout Command
+        '''
+        # TODO
+        return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
+
+    def on_hci_read_local_version_information_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.4.1 Read Local Version Information Command
+        '''
+        return struct.pack('<BBHBHH',
+                           HCI_SUCCESS,
+                           self.hci_version,
+                           self.hci_revision,
+                           self.lmp_version,
+                           self.manufacturer_name,
+                           self.lmp_subversion)
+
+    def on_hci_read_local_supported_commands_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.4.2 Read Local Supported Commands Command
+        '''
+        return bytes([HCI_SUCCESS]) + self.supported_commands
+
+    def on_hci_read_local_supported_features_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.4.3 Read Local Supported Features Command
+        '''
+        return bytes([HCI_SUCCESS]) + self.lmp_features
+
+    def on_hci_read_bd_addr_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.4.6 Read BD_ADDR Command
+        '''
+        bd_addr = self._public_address.to_bytes() if self._public_address is not None else bytes(6)
+        return bytes([HCI_SUCCESS]) + bd_addr
+
+    def on_hci_le_set_event_mask_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.1 LE Set Event Mask Command
+        '''
+        self.le_event_mask = command.le_event_mask
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_buffer_size_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.2 LE Read Buffer Size Command
+        '''
+        return struct.pack('<BHB',
+                           HCI_SUCCESS,
+                           self.hc_le_data_packet_length,
+                           self.hc_total_num_le_data_packets)
+
+    def on_hci_le_read_local_supported_features_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.3 LE Read Local Supported Features Command
+        '''
+        return bytes([HCI_SUCCESS]) + self.le_features
+
+    def on_hci_le_set_random_address_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.4 LE Set Random Address Command
+        '''
+        self.random_address = command.random_address
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_set_advertising_parameters_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.5 LE Set Advertising Parameters Command
+        '''
+        self.advertising_parameters = command
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_advertising_channel_tx_power_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.6 LE Read Advertising Channel Tx Power Command
+        '''
+        return bytes([HCI_SUCCESS, self.avertising_channel_tx_power])
+
+    def on_hci_le_set_advertising_data_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.7 LE Set Advertising Data Command
+        '''
+        self.advertising_data = command.advertising_data
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_set_scan_response_data_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.8 LE Set Scan Response Data Command
+        '''
+        self.le_scan_response_data = command.scan_response_data
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_set_advertising_enable_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.9 LE Set Advertising Enable Command
+        '''
+        if command.advertising_enable:
+            self.start_advertising()
+        else:
+            self.stop_advertising()
+
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_set_scan_parameters_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.10 LE Set Scan Parameters Command
+        '''
+        self.le_scan_type              = command.le_scan_type
+        self.le_scan_interval          = command.le_scan_interval
+        self.le_scan_window            = command.le_scan_window
+        self.le_scan_own_address_type  = command.own_address_type
+        self.le_scanning_filter_policy = command.scanning_filter_policy
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_set_scan_enable_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.11 LE Set Scan Enable Command
+        '''
+        self.le_scan_enable    = command.le_scan_enable
+        self.filter_duplicates = command.filter_duplicates
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_create_connection_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.12 LE Create Connection Command
+        '''
+
+        if not self.link:
+            return
+
+        logger.debug(f'Connection request to {command.peer_address}')
+
+        # Check that we don't already have a pending connection
+        if self.link.get_pending_connection():
+            self.send_hci_packet(HCI_Command_Status_Event(
+                status                  = HCI_COMMAND_DISALLOWED_ERROR,
+                num_hci_command_packets = 1,
+                command_opcode          = command.op_code
+            ))
+            return
+
+        # Initiate the connection
+        self.link.connect(self.random_address, command)
+
+        # Say that the connection is pending
+        self.send_hci_packet(HCI_Command_Status_Event(
+            status                  = HCI_COMMAND_STATUS_PENDING,
+            num_hci_command_packets = 1,
+            command_opcode          = command.op_code
+        ))
+
+    def on_hci_le_create_connection_cancel_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.13 LE Create Connection Cancel Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_white_list_size_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.14 LE Read White List Size Command
+        '''
+        return bytes([HCI_SUCCESS, self.white_list_size])
+
+    def on_hci_le_clear_white_list_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.15 LE Clear White List Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_add_device_to_white_list_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.16 LE Add Device To White List Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_remove_device_from_white_list_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.17 LE Remove Device From White List Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_remote_features_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.21 LE Read Remote Features Command
+        '''
+
+        # First, say that the command is pending
+        self.send_hci_packet(HCI_Command_Status_Event(
+            status                  = HCI_COMMAND_STATUS_PENDING,
+            num_hci_command_packets = 1,
+            command_opcode          = command.op_code
+        ))
+
+        # Then send the remote features
+        self.send_hci_packet(HCI_LE_Read_Remote_Features_Complete_Event(
+            status            = HCI_SUCCESS,
+            connection_handle = 0,
+            le_features       = bytes.fromhex('dd40000000000000')
+        ))
+
+    def on_hci_le_rand_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.23 LE Rand Command
+        '''
+        return bytes([HCI_SUCCESS]) + struct.pack('Q', random.randint(0, 1 << 64))
+
+    def on_hci_le_start_encryption_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.24 LE Start Encryption Command
+        '''
+
+        # Check the parameters
+        if not (connection := self.find_central_connection_by_handle(command.connection_handle)):
+            logger.warn('connection not found')
+            return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
+
+        # Notify that the connection is now encrypted
+        self.link.on_connection_encrypted(
+            self.random_address,
+            connection.peer_address,
+            command.random_number,
+            command.encrypted_diversifier,
+            command.long_term_key
+        )
+
+        self.send_hci_packet(HCI_Command_Status_Event(
+            status                  = HCI_COMMAND_STATUS_PENDING,
+            num_hci_command_packets = 1,
+            command_opcode          = command.op_code
+        ))
+
+    def on_hci_le_read_supported_states_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.27 LE Read Supported States Command
+        '''
+        return bytes([HCI_SUCCESS]) + self.le_states
+
+    def on_hci_le_read_suggested_default_data_length_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.34 LE Read Suggested Default Data Length Command
+        '''
+        return struct.pack('<BHH',
+                           HCI_SUCCESS,
+                           self.suggested_max_tx_octets,
+                           self.suggested_max_tx_time)
+
+    def on_hci_le_write_suggested_default_data_length_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.35 LE Write Suggested Default Data Length Command
+        '''
+        self.suggested_max_tx_octets, self.suggested_max_tx_time = struct.unpack('<HH', command.parameters[:4])
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_local_p_256_public_key_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.36 LE Read P-256 Public Key Command
+        '''
+        # TODO create key and send HCI_LE_Read_Local_P-256_Public_Key_Complete event
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_add_device_to_resolving_list_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.38 LE Add Device To Resolving List Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_clear_resolving_list_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.40 LE Clear Resolving List Command
+        '''
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_resolving_list_size_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.41 LE Read Resolving List Size Command
+        '''
+        return bytes([HCI_SUCCESS, self.resolving_list_size])
+
+    def on_hci_le_set_address_resolution_enable_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.44 LE Set Address Resolution Enable Command
+        '''
+        ret = HCI_SUCCESS
+        if command.address_resolution == 1:
+            self.le_address_resolution = True
+        elif command.address_resolution == 0:
+            self.le_address_resolution = False
+        else:
+            ret = HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR
+        return bytes([ret])
+
+    def on_hci_le_set_resolvable_private_address_timeout_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.45 LE Set Resolvable Private Address Timeout Command
+        '''
+        self.le_rpa_timeout = command.rpa_timeout
+        return bytes([HCI_SUCCESS])
+
+    def on_hci_le_read_maximum_data_length_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.46 LE Read Maximum Data Length Command
+        '''
+        return struct.pack('<BHHHH',
+                           HCI_SUCCESS,
+                           self.supported_max_tx_octets,
+                           self.supported_max_tx_time,
+                           self.supported_max_rx_octets,
+                           self.supported_max_rx_time)
+
+    def on_hci_le_set_default_phy_command(self, command):
+        '''
+        See Bluetooth spec Vol 2, Part E - 7.8.48 LE Set Default PHY Command
+        '''
+        self.default_phy = {
+            'all_phys': command.all_phys,
+            'tx_phys':  command.tx_phys,
+            'rx_phys':  command.rx_phys
+        }
+        return bytes([HCI_SUCCESS])
diff --git a/bumble/core.py b/bumble/core.py
new file mode 100644
index 0000000..746a601
--- /dev/null
+++ b/bumble/core.py
@@ -0,0 +1,852 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import struct
+
+from .company_ids import COMPANY_IDENTIFIERS
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+BT_CENTRAL_ROLE    = 0
+BT_PERIPHERAL_ROLE = 1
+
+BT_BR_EDR_TRANSPORT = 0
+BT_LE_TRANSPORT     = 1
+
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+def bit_flags_to_strings(bits, bit_flag_names):
+    names = []
+    index = 0
+    while bits != 0:
+        if bits & 1:
+            name = bit_flag_names[index] if index < len(bit_flag_names) else f'#{index}'
+            names.append(name)
+        bits >>= 1
+        index += 1
+
+    return names
+
+
+def name_or_number(dictionary, number, width=2):
+    name = dictionary.get(number)
+    if name is not None:
+        return name
+    return f'[0x{number:0{width}X}]'
+
+
+def padded_bytes(buffer, size):
+    padding_size = max(size - len(buffer), 0)
+    return buffer + bytes(padding_size)
+
+
+# -----------------------------------------------------------------------------
+# Exceptions
+# -----------------------------------------------------------------------------
+class BaseError(Exception):
+    """ Base class for errors with an error code, error name and namespace"""
+    def __init__(self, error_code, error_namespace='', error_name='', details=''):
+        super().__init__()
+        self.error_code      = error_code
+        self.error_namespace = error_namespace
+        self.error_name      = error_name
+        self.details         = details
+
+    def __str__(self):
+        if self.error_namespace:
+            namespace = f'{self.error_namespace}/'
+        else:
+            namespace = ''
+        if self.error_name:
+            name = f'{self.error_name} [0x{self.error_code:X}]'
+        else:
+            name = f'0x{self.error_code:X}'
+
+        return f'{type(self).__name__}({namespace}{name})'
+
+
+class ProtocolError(BaseError):
+    """ Protocol Error """
+
+
+class TimeoutError(Exception):
+    """ Timeout Error """
+
+
+class InvalidStateError(Exception):
+    """ Invalid State Error """
+
+
+class ConnectionError(BaseError):
+    """ Connection Error """
+    FAILURE            = 0x01
+    CONNECTION_REFUSED = 0x02
+
+
+# -----------------------------------------------------------------------------
+# UUID
+#
+# NOTE: the internal byte representation is in little-endian byte order
+#
+# Base UUID: 00000000-0000-1000-8000- 00805F9B34FB
+# -----------------------------------------------------------------------------
+class UUID:
+    '''
+    See Bluetooth spec Vol 3, Part B - 2.5.1 UUID
+    '''
+    BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')
+    UUIDS = []  # Registry of all instances created
+
+    def __init__(self, uuid_str_or_int, name = None):
+        if type(uuid_str_or_int) is int:
+            self.uuid_bytes = struct.pack('<H', uuid_str_or_int)
+        else:
+            if len(uuid_str_or_int) == 36:
+                if uuid_str_or_int[8] != '-' or uuid_str_or_int[13] != '-' or uuid_str_or_int[18] != '-' or uuid_str_or_int[23] != '-':
+                    raise ValueError('invalid UUID format')
+                uuid_str = uuid_str_or_int.replace('-', '')
+            else:
+                uuid_str = uuid_str_or_int
+            if len(uuid_str) != 32 and len(uuid_str) != 8 and len(uuid_str) != 4:
+                raise ValueError('invalid UUID format')
+            self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str)))
+        self.name = name
+
+    def register(self):
+        # Register this object in the class registry, and update the entry's name if it wasn't set already
+        for uuid in self.UUIDS:
+            if self == uuid:
+                if uuid.name is None:
+                    uuid.name = self.name
+                return uuid
+
+        self.UUIDS.append(self)
+        return self
+
+    @classmethod
+    def from_bytes(cls, uuid_bytes, name = None):
+        if len(uuid_bytes) in {2, 4, 16}:
+            self = cls.__new__(cls)
+            self.uuid_bytes = uuid_bytes
+            self.name = name
+
+            return self.register()
+        else:
+            raise ValueError('only 2, 4 and 16 bytes are allowed')
+
+    @classmethod
+    def from_16_bits(cls, uuid_16, name = None):
+        return cls.from_bytes(struct.pack('<H', uuid_16), name)
+
+    @classmethod
+    def from_32_bits(cls, uuid_32, name = None):
+        return cls.from_bytes(struct.pack('<I', uuid_32), name)
+
+    @classmethod
+    def parse_uuid(cls, bytes, offset):
+        return len(bytes), cls.from_bytes(bytes[offset:])
+
+    @classmethod
+    def parse_uuid_2(cls, bytes, offset):
+        return offset + 2, cls.from_bytes(bytes[offset:offset + 2])
+
+    def to_bytes(self, force_128 = False):
+        if len(self.uuid_bytes) == 16 or not force_128:
+            return self.uuid_bytes
+        elif len(self.uuid_bytes) == 4:
+            return self.uuid_bytes + UUID.BASE_UUID
+        else:
+            return self.uuid_bytes + bytes([0, 0]) + UUID.BASE_UUID
+
+    def to_pdu_bytes(self):
+        '''
+        Convert to bytes for use in an ATT PDU.
+        According to Vol 3, Part F - 3.2.1 Attribute Type:
+        "All 32-bit Attribute UUIDs shall be converted to 128-bit UUIDs when the
+         Attribute UUID is contained in an ATT PDU."
+        '''
+        return self.to_bytes(force_128 = (len(self.uuid_bytes) == 4))
+
+    def to_hex_str(self):
+        if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4:
+            return bytes(reversed(self.uuid_bytes)).hex().upper()
+        else:
+            return ''.join([
+                bytes(reversed(self.uuid_bytes[12:16])).hex(),
+                bytes(reversed(self.uuid_bytes[10:12])).hex(),
+                bytes(reversed(self.uuid_bytes[8:10])).hex(),
+                bytes(reversed(self.uuid_bytes[6:8])).hex(),
+                bytes(reversed(self.uuid_bytes[0:6])).hex()
+            ]).upper()
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __eq__(self, other):
+        if isinstance(other, UUID):
+            return self.to_bytes(force_128 = True) == other.to_bytes(force_128 = True)
+        elif type(other) is str:
+            return UUID(other) == self
+
+        return False
+
+    def __hash__(self):
+        return hash(self.uuid_bytes)
+
+    def __str__(self):
+        if len(self.uuid_bytes) == 2:
+            v = struct.unpack('<H', self.uuid_bytes)[0]
+            result = f'UUID-16:{v:04X}'
+        elif len(self.uuid_bytes) == 4:
+            v = struct.unpack('<I', self.uuid_bytes)[0]
+            result = f'UUID-32:{v:08X}'
+        else:
+            result = '-'.join([
+                bytes(reversed(self.uuid_bytes[12:16])).hex(),
+                bytes(reversed(self.uuid_bytes[10:12])).hex(),
+                bytes(reversed(self.uuid_bytes[8:10])).hex(),
+                bytes(reversed(self.uuid_bytes[6:8])).hex(),
+                bytes(reversed(self.uuid_bytes[0:6])).hex()
+            ]).upper()
+        if self.name is not None:
+            return result + f' ({self.name})'
+        else:
+            return result
+
+    def __repr__(self):
+        return str(self)
+
+
+# -----------------------------------------------------------------------------
+# Common UUID constants
+# -----------------------------------------------------------------------------
+
+# Protocol Identifiers
+BT_SDP_PROTOCOL_ID                      = UUID.from_16_bits(0x0001, 'SDP')
+BT_UDP_PROTOCOL_ID                      = UUID.from_16_bits(0x0002, 'UDP')
+BT_RFCOMM_PROTOCOL_ID                   = UUID.from_16_bits(0x0003, 'RFCOMM')
+BT_TCP_PROTOCOL_ID                      = UUID.from_16_bits(0x0004, 'TCP')
+BT_TCS_BIN_PROTOCOL_ID                  = UUID.from_16_bits(0x0005, 'TCP-BIN')
+BT_TCS_AT_PROTOCOL_ID                   = UUID.from_16_bits(0x0006, 'TCS-AT')
+BT_ATT_PROTOCOL_ID                      = UUID.from_16_bits(0x0007, 'ATT')
+BT_OBEX_PROTOCOL_ID                     = UUID.from_16_bits(0x0008, 'OBEX')
+BT_IP_PROTOCOL_ID                       = UUID.from_16_bits(0x0009, 'IP')
+BT_FTP_PROTOCOL_ID                      = UUID.from_16_bits(0x000A, 'FTP')
+BT_HTTP_PROTOCOL_ID                     = UUID.from_16_bits(0x000C, 'HTTP')
+BT_WSP_PROTOCOL_ID                      = UUID.from_16_bits(0x000E, 'WSP')
+BT_BNEP_PROTOCOL_ID                     = UUID.from_16_bits(0x000F, 'BNEP')
+BT_UPNP_PROTOCOL_ID                     = UUID.from_16_bits(0x0010, 'UPNP')
+BT_HIDP_PROTOCOL_ID                     = UUID.from_16_bits(0x0011, 'HIDP')
+BT_HARDCOPY_CONTROL_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x0012, 'HardcopyControlChannel')
+BT_HARDCOPY_DATA_CHANNEL_PROTOCOL_ID    = UUID.from_16_bits(0x0014, 'HardcopyDataChannel')
+BT_HARDCOPY_NOTIFICATION_PROTOCOL_ID    = UUID.from_16_bits(0x0016, 'HardcopyNotification')
+BT_AVTCP_PROTOCOL_ID                    = UUID.from_16_bits(0x0017, 'AVCTP')
+BT_AVDTP_PROTOCOL_ID                    = UUID.from_16_bits(0x0019, 'AVDTP')
+BT_CMTP_PROTOCOL_ID                     = UUID.from_16_bits(0x001B, 'CMTP')
+BT_MCAP_CONTROL_CHANNEL_PROTOCOL_ID     = UUID.from_16_bits(0x001E, 'MCAPControlChannel')
+BT_MCAP_DATA_CHANNEL_PROTOCOL_ID        = UUID.from_16_bits(0x001F, 'MCAPDataChannel')
+BT_L2CAP_PROTOCOL_ID                    = UUID.from_16_bits(0x0100, 'L2CAP')
+
+# Service Classes and Profiles
+BT_SERVICE_DISCOVERY_SERVER_SERVICE_CLASS_ID_SERVICE = UUID.from_16_bits(0x1000, 'ServiceDiscoveryServerServiceClassID')
+BT_BROWSE_GROUP_DESCRIPTOR_SERVICE_CLASS_ID_SERVICE  = UUID.from_16_bits(0x1001, 'BrowseGroupDescriptorServiceClassID')
+BT_SERIAL_PORT_SERVICE                               = UUID.from_16_bits(0x1101, 'SerialPort')
+BT_LAN_ACCESS_USING_PPP_SERVICE                      = UUID.from_16_bits(0x1102, 'LANAccessUsingPPP')
+BT_DIALUP_NETWORKING_SERVICE                         = UUID.from_16_bits(0x1103, 'DialupNetworking')
+BT_IR_MCSYNC_SERVICE                                 = UUID.from_16_bits(0x1104, 'IrMCSync')
+BT_OBEX_OBJECT_PUSH_SERVICE                          = UUID.from_16_bits(0x1105, 'OBEXObjectPush')
+BT_OBEX_FILE_TRANSFER_SERVICE                        = UUID.from_16_bits(0x1106, 'OBEXFileTransfer')
+BT_IR_MCSYNC_COMMAND_SERVICE                         = UUID.from_16_bits(0x1107, 'IrMCSyncCommand')
+BT_HEADSET_SERVICE                                   = UUID.from_16_bits(0x1108, 'Headset')
+BT_CORDLESS_TELEPHONY_SERVICE                        = UUID.from_16_bits(0x1109, 'CordlessTelephony')
+BT_AUDIO_SOURCE_SERVICE                              = UUID.from_16_bits(0x110A, 'AudioSource')
+BT_AUDIO_SINK_SERVICE                                = UUID.from_16_bits(0x110B, 'AudioSink')
+BT_AV_REMOTE_CONTROL_TARGET_SERVICE                  = UUID.from_16_bits(0x110C, 'A/V_RemoteControlTarget')
+BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE               = UUID.from_16_bits(0x110D, 'AdvancedAudioDistribution')
+BT_AV_REMOTE_CONTROL_SERVICE                         = UUID.from_16_bits(0x110E, 'A/V_RemoteControl')
+BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE              = UUID.from_16_bits(0x110F, 'A/V_RemoteControlController')
+BT_INTERCOM_SERVICE                                  = UUID.from_16_bits(0x1110, 'Intercom')
+BT_FAX_SERVICE                                       = UUID.from_16_bits(0x1111, 'Fax')
+BT_HEADSET_AUDIO_GATEWAY_SERVICE                     = UUID.from_16_bits(0x1112, 'Headset - Audio Gateway')
+BT_WAP_SERVICE                                       = UUID.from_16_bits(0x1113, 'WAP')
+BT_WAP_CLIENT_SERVICE                                = UUID.from_16_bits(0x1114, 'WAP_CLIENT')
+BT_PANU_SERVICE                                      = UUID.from_16_bits(0x1115, 'PANU')
+BT_NAP_SERVICE                                       = UUID.from_16_bits(0x1116, 'NAP')
+BT_GN_SERVICE                                        = UUID.from_16_bits(0x1117, 'GN')
+BT_DIRECT_PRINTING_SERVICE                           = UUID.from_16_bits(0x1118, 'DirectPrinting')
+BT_REFERENCE_PRINTING_SERVICE                        = UUID.from_16_bits(0x1119, 'ReferencePrinting')
+BT_BASIC_IMAGING_PROFILE_SERVICE                     = UUID.from_16_bits(0x111A, 'Basic Imaging Profile')
+BT_IMAGING_RESPONDER_SERVICE                         = UUID.from_16_bits(0x111B, 'ImagingResponder')
+BT_IMAGING_AUTOMATIC_ARCHIVE_SERVICE                 = UUID.from_16_bits(0x111C, 'ImagingAutomaticArchive')
+BT_IMAGING_REFERENCED_OBJECTS_SERVICE                = UUID.from_16_bits(0x111D, 'ImagingReferencedObjects')
+BT_HANDSFREE_SERVICE                                 = UUID.from_16_bits(0x111E, 'Handsfree')
+BT_HANDSFREE_AUDIO_GATEWAY_SERVICE                   = UUID.from_16_bits(0x111F, 'HandsfreeAudioGateway')
+BT_DIRECT_PRINTING_REFERENCE_OBJECTS_SERVICE         = UUID.from_16_bits(0x1120, 'DirectPrintingReferenceObjectsService')
+BT_REFLECTED_UI_SERVICE                              = UUID.from_16_bits(0x1121, 'ReflectedUI')
+BT_BASIC_PRINTING_SERVICE                            = UUID.from_16_bits(0x1122, 'BasicPrinting')
+BT_PRINTING_STATUS_SERVICE                           = UUID.from_16_bits(0x1123, 'PrintingStatus')
+BT_HUMAN_INTERFACE_DEVICE_SERVICE                    = UUID.from_16_bits(0x1124, 'HumanInterfaceDeviceService')
+BT_HARDCOPY_CABLE_REPLACEMENT_SERVICE                = UUID.from_16_bits(0x1125, 'HardcopyCableReplacement')
+BT_HCR_PRINT_SERVICE                                 = UUID.from_16_bits(0x1126, 'HCR_Print')
+BT_HCR_SCAN_SERVICE                                  = UUID.from_16_bits(0x1127, 'HCR_Scan')
+BT_COMMON_ISDN_ACCESS_SERVICE                        = UUID.from_16_bits(0x1128, 'Common_ISDN_Access')
+BT_SIM_ACCESS_SERVICE                                = UUID.from_16_bits(0x112D, 'SIM_Access')
+BT_PHONEBOOK_ACCESS_PCE_SERVICE                      = UUID.from_16_bits(0x112E, 'Phonebook Access - PCE')
+BT_PHONEBOOK_ACCESS_PSE_SERVICE                      = UUID.from_16_bits(0x112F, 'Phonebook Access - PSE')
+BT_PHONEBOOK_ACCESS_SERVICE                          = UUID.from_16_bits(0x1130, 'Phonebook Access')
+BT_HEADSET_HS_SERVICE                                = UUID.from_16_bits(0x1131, 'Headset - HS')
+BT_MESSAGE_ACCESS_SERVER_SERVICE                     = UUID.from_16_bits(0x1132, 'Message Access Server')
+BT_MESSAGE_NOTIFICATION_SERVER_SERVICE               = UUID.from_16_bits(0x1133, 'Message Notification Server')
+BT_MESSAGE_ACCESS_PROFILE_SERVICE                    = UUID.from_16_bits(0x1134, 'Message Access Profile')
+BT_GNSS_SERVICE                                      = UUID.from_16_bits(0x1135, 'GNSS')
+BT_GNSS_SERVER_SERVICE                               = UUID.from_16_bits(0x1136, 'GNSS_Server')
+BT_3D_DISPLAY_SERVICE                                = UUID.from_16_bits(0x1137, '3D Display')
+BT_3D_GLASSES_SERVICE                                = UUID.from_16_bits(0x1138, '3D Glasses')
+BT_3D_SYNCHRONIZATION_SERVICE                        = UUID.from_16_bits(0x1139, '3D Synchronization')
+BT_MPS_PROFILE_SERVICE                               = UUID.from_16_bits(0x113A, 'MPS Profile')
+BT_MPS_SC_SERVICE                                    = UUID.from_16_bits(0x113B, 'MPS SC')
+BT_ACCESS_SERVICE_SERVICE                            = UUID.from_16_bits(0x113C, 'CTN Access Service')
+BT_CTN_NOTIFICATION_SERVICE_SERVICE                  = UUID.from_16_bits(0x113D, 'CTN Notification Service')
+BT_CTN_PROFILE_SERVICE                               = UUID.from_16_bits(0x113E, 'CTN Profile')
+BT_PNP_INFORMATION_SERVICE                           = UUID.from_16_bits(0x1200, 'PnPInformation')
+BT_GENERIC_NETWORKING_SERVICE                        = UUID.from_16_bits(0x1201, 'GenericNetworking')
+BT_GENERIC_FILE_TRANSFER_SERVICE                     = UUID.from_16_bits(0x1202, 'GenericFileTransfer')
+BT_GENERIC_AUDIO_SERVICE                             = UUID.from_16_bits(0x1203, 'GenericAudio')
+BT_GENERIC_TELEPHONY_SERVICE                         = UUID.from_16_bits(0x1204, 'GenericTelephony')
+BT_UPNP_SERVICE                                      = UUID.from_16_bits(0x1205, 'UPNP_Service')
+BT_UPNP_IP_SERVICE                                   = UUID.from_16_bits(0x1206, 'UPNP_IP_Service')
+BT_ESDP_UPNP_IP_PAN_SERVICE                          = UUID.from_16_bits(0x1300, 'ESDP_UPNP_IP_PAN')
+BT_ESDP_UPNP_IP_LAP_SERVICE                          = UUID.from_16_bits(0x1301, 'ESDP_UPNP_IP_LAP')
+BT_ESDP_UPNP_L2CAP_SERVICE                           = UUID.from_16_bits(0x1302, 'ESDP_UPNP_L2CAP')
+BT_VIDEO_SOURCE_SERVICE                              = UUID.from_16_bits(0x1303, 'VideoSource')
+BT_VIDEO_SINK_SERVICE                                = UUID.from_16_bits(0x1304, 'VideoSink')
+BT_VIDEO_DISTRIBUTION_SERVICE                        = UUID.from_16_bits(0x1305, 'VideoDistribution')
+BT_HDP_SERVICE                                       = UUID.from_16_bits(0x1400, 'HDP')
+BT_HDP_SOURCE_SERVICE                                = UUID.from_16_bits(0x1401, 'HDP Source')
+BT_HDP_SINK_SERVICE                                  = UUID.from_16_bits(0x1402, 'HDP Sink')
+
+
+# -----------------------------------------------------------------------------
+# DeviceClass
+# -----------------------------------------------------------------------------
+class DeviceClass:
+    # Major Service Classes (flags combined with OR)
+    LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = (1 << 0)
+    LE_AUDIO_SERVICE_CLASS                  = (1 << 1)
+    RESERVED                                = (1 << 2)
+    POSITIONING_SERVICE_CLASS               = (1 << 3)
+    NETWORKING_SERVICE_CLASS                = (1 << 4)
+    RENDERING_SERVICE_CLASS                 = (1 << 5)
+    CAPTURING_SERVICE_CLASS                 = (1 << 6)
+    OBJECT_TRANSFER_SERVICE_CLASS           = (1 << 7)
+    AUDIO_SERVICE_CLASS                     = (1 << 8)
+    TELEPHONY_SERVICE_CLASS                 = (1 << 9)
+    INFORMATION_SERVICE_CLASS               = (1 << 10)
+
+    SERVICE_CLASS_LABELS = [
+        'Limited Discoverable Mode',
+        'LE audio',
+        '(reserved)',
+        'Positioning',
+        'Networking',
+        'Rendering',
+        'Capturing',
+        'Object Transfer',
+        'Audio',
+        'Telephony',
+        'Information'
+    ]
+
+    # Major Device Classes
+    MISCELLANEOUS_MAJOR_DEVICE_CLASS            = 0x00
+    COMPUTER_MAJOR_DEVICE_CLASS                 = 0x01
+    PHONE_MAJOR_DEVICE_CLASS                    = 0x02
+    LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS = 0x03
+    AUDIO_VIDEO_MAJOR_DEVICE_CLASS              = 0x04
+    PERIPHERAL_MAJOR_DEVICE_CLASS               = 0x05
+    IMAGING_MAJOR_DEVICE_CLASS                  = 0x06
+    WEARABLE_MAJOR_DEVICE_CLASS                 = 0x07
+    TOY_MAJOR_DEVICE_CLASS                      = 0x08
+    HEALTH_MAJOR_DEVICE_CLASS                   = 0x09
+    UNCATEGORIZED_MAJOR_DEVICE_CLASS            = 0x1F
+
+    MAJOR_DEVICE_CLASS_NAMES = {
+        MISCELLANEOUS_MAJOR_DEVICE_CLASS:            'Miscellaneous',
+        COMPUTER_MAJOR_DEVICE_CLASS:                 'Computer',
+        PHONE_MAJOR_DEVICE_CLASS:                    'Phone',
+        LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS: 'LAN/Network Access Point',
+        AUDIO_VIDEO_MAJOR_DEVICE_CLASS:              'Audio/Video',
+        PERIPHERAL_MAJOR_DEVICE_CLASS:               'Peripheral',
+        IMAGING_MAJOR_DEVICE_CLASS:                  'Imaging',
+        WEARABLE_MAJOR_DEVICE_CLASS:                 'Wearable',
+        TOY_MAJOR_DEVICE_CLASS:                      'Toy',
+        HEALTH_MAJOR_DEVICE_CLASS:                   'Health',
+        UNCATEGORIZED_MAJOR_DEVICE_CLASS:            'Uncategorized'
+    }
+
+    COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS         = 0x00
+    COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS   = 0x01
+    COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS = 0x02
+    COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS       = 0x03
+    COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS       = 0x04
+    COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS      = 0x05
+    COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS     = 0x06
+    COMPUTER_TABLET_MINOR_DEVICE_CLASS                = 0x07
+
+    COMPUTER_MINOR_DEVICE_CLASS_NAMES = {
+        COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS:         'Uncategorized',
+        COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS:   'Desktop workstation',
+        COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS: 'Server-class computer',
+        COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS:       'Laptop',
+        COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS:       'Handheld PC/PDA',
+        COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS:      'Palm-size PC/PDA',
+        COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS:     'Wearable computer',
+        COMPUTER_TABLET_MINOR_DEVICE_CLASS:                'Tablet'
+    }
+
+    PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS                = 0x00
+    PHONE_CELLULAR_MINOR_DEVICE_CLASS                     = 0x01
+    PHONE_CORDLESS_MINOR_DEVICE_CLASS                     = 0x02
+    PHONE_SMARTPHONE_MINOR_DEVICE_CLASS                   = 0x03
+    PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS = 0x04
+    PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS                  = 0x05
+
+    PHONE_MINOR_DEVICE_CLASS_NAMES = {
+        PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS:                'Uncategorized',
+        PHONE_CELLULAR_MINOR_DEVICE_CLASS:                     'Cellular',
+        PHONE_CORDLESS_MINOR_DEVICE_CLASS:                     'Cordless',
+        PHONE_SMARTPHONE_MINOR_DEVICE_CLASS:                   'Smartphone',
+        PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS: 'Wired modem or voice gateway',
+        PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS:                  'Common ISDN access'
+    }
+
+    AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS                 = 0x00
+    AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS       = 0x01
+    AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS             = 0x02
+    # (RESERVED)                                                 = 0x03
+    AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS                    = 0x04
+    AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS                   = 0x05
+    AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS                    = 0x06
+    AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS                = 0x07
+    AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS                     = 0x08
+    AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS                   = 0x09
+    AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS             = 0x0A
+    AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS                           = 0x0B
+    AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS                  = 0x0C
+    AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS                     = 0x0D
+    AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS                 = 0x0E
+    AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x0F
+    AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS            = 0x10
+    # (RESERVED)                                                 = 0x11
+    AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS                 = 0x12
+
+    AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES = {
+        AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS:                 'Uncategorized',
+        AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS:       'Wearable Headset Device',
+        AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS:             'Hands-free Device',
+        AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS:                    'Microphone',
+        AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS:                   'Loudspeaker',
+        AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS:                    'Headphones',
+        AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS:                'Portable Audio',
+        AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS:                     'Car audio',
+        AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS:                   'Set-top box',
+        AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS:             'HiFi Audio Device',
+        AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS:                           'VCR',
+        AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS:                  'Video Camera',
+        AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS:                     'Camcorder',
+        AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS:                 'Video Monitor',
+        AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Video Display and Loudspeaker',
+        AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS:            'Video Conferencing',
+        AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS:                 'Gaming/Toy'
+    }
+
+    PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS                  = 0x00
+    PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS                       = 0x10
+    PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS                = 0x20
+    PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x30
+    PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS                       = 0x01
+    PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS                        = 0x02
+    PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS                 = 0x03
+    PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS                 = 0x04
+    PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS               = 0x05
+    PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS                    = 0x06
+    PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS                    = 0x07
+    PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS               = 0x08
+    PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS = 0x09
+
+    PERIPHERAL_MINOR_DEVICE_CLASS_NAMES = {
+        PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS:                  'Uncategorized',
+        PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS:                       'Keyboard',
+        PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS:                'Pointing device',
+        PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Combo keyboard/pointing device',
+        PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS:                       'Joystick',
+        PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS:                        'Gamepad',
+        PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS:                 'Remote control',
+        PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS:                 'Sensing device',
+        PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS:               'Digitizer tablet',
+        PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS:                    'Card Reader',
+        PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS:                    'Digital Pen',
+        PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS:               'Handheld scanner',
+        PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS: 'Handheld gestural input device'
+    }
+
+    MINOR_DEVICE_CLASS_NAMES = {
+        COMPUTER_MAJOR_DEVICE_CLASS:    COMPUTER_MINOR_DEVICE_CLASS_NAMES,
+        PHONE_MAJOR_DEVICE_CLASS:       PHONE_MINOR_DEVICE_CLASS_NAMES,
+        AUDIO_VIDEO_MAJOR_DEVICE_CLASS: AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES,
+        PERIPHERAL_MAJOR_DEVICE_CLASS:  PERIPHERAL_MINOR_DEVICE_CLASS_NAMES
+    }
+
+    @staticmethod
+    def split_class_of_device(class_of_device):
+        # Split the bit fields of the composite class of device value into:
+        # (service_classes, major_device_class, minor_device_class)
+        return ((class_of_device >> 13 & 0x7FF), (class_of_device >> 8 & 0x1F), (class_of_device >> 2 & 0x3F))
+
+    @staticmethod
+    def pack_class_of_device(service_classes, major_device_class, minor_device_class):
+        return service_classes << 13 | major_device_class << 8 | minor_device_class << 2
+
+    @staticmethod
+    def service_class_labels(service_class_flags):
+        return bit_flags_to_strings(service_class_flags, DeviceClass.SERVICE_CLASS_LABELS)
+
+    @staticmethod
+    def major_device_class_name(device_class):
+        return name_or_number(DeviceClass.MAJOR_DEVICE_CLASS_NAMES, device_class)
+
+    @staticmethod
+    def minor_device_class_name(major_device_class, minor_device_class):
+        class_names = DeviceClass.MINOR_DEVICE_CLASS_NAMES.get(major_device_class)
+        if class_names is None:
+            return f'#{minor_device_class:02X}'
+        return name_or_number(class_names, minor_device_class)
+
+
+# -----------------------------------------------------------------------------
+# Advertising Data
+# -----------------------------------------------------------------------------
+class AdvertisingData:
+    # This list is only partial, it still needs to be filled in from the spec
+    FLAGS                                          = 0x01
+    INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS  = 0x02
+    COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS    = 0x03
+    INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS  = 0x04
+    COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS    = 0x05
+    INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = 0x06
+    COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS   = 0x07
+    SHORTENED_LOCAL_NAME                           = 0x08
+    COMPLETE_LOCAL_NAME                            = 0x09
+    TX_POWER_LEVEL                                 = 0x0A
+    CLASS_OF_DEVICE                                = 0x0D
+    SIMPLE_PAIRING_HASH_C                          = 0x0E
+    SIMPLE_PAIRING_HASH_C_192                      = 0x0E
+    SIMPLE_PAIRING_RANDOMIZER_R                    = 0x0F
+    SIMPLE_PAIRING_RANDOMIZER_R_192                = 0x0F
+    DEVICE_ID                                      = 0x10
+    SECURITY_MANAGER_TK_VALUE                      = 0x10
+    SECURITY_MANAGER_OUT_OF_BAND_FLAGS             = 0x11
+    PERIPHERAL_CONNECTION_INTERVAL_RANGE           = 0x12
+    LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS      = 0x14
+    LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS     = 0x15
+    SERVICE_DATA                                   = 0x16
+    SERVICE_DATA_16_BIT_UUID                       = 0x16
+    PUBLIC_TARGET_ADDRESS                          = 0x17
+    RANDOM_TARGET_ADDRESS                          = 0x18
+    APPEARANCE                                     = 0x19
+    ADVERTISING_INTERVAL                           = 0x1A
+    LE_BLUETOOTH_DEVICE_ADDRESS                    = 0x1B
+    LE_ROLE                                        = 0x1C
+    SIMPLE_PAIRING_HASH_C_256                      = 0x1D
+    SIMPLE_PAIRING_RANDOMIZER_R_256                = 0x1E
+    LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS      = 0x1F
+    SERVICE_DATA_32_BIT_UUID                       = 0x20
+    SERVICE_DATA_128_BIT_UUID                      = 0x21
+    LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE       = 0x22
+    LE_SECURE_CONNECTIONS_RANDOM_VALUE             = 0x23
+    URI                                            = 0x24
+    INDOOR_POSITIONING                             = 0x25
+    TRANSPORT_DISCOVERY_DATA                       = 0x26
+    LE_SUPPORTED_FEATURES                          = 0x27
+    CHANNEL_MAP_UPDATE_INDICATION                  = 0x28
+    PB_ADV                                         = 0x29
+    MESH_MESSAGE                                   = 0x2A
+    MESH_BEACON                                    = 0x2B
+    BIGINFO                                        = 0x2C
+    BROADCAST_CODE                                 = 0x2D
+    RESOLVABLE_SET_IDENTIFIER                      = 0x2E
+    ADVERTISING_INTERVAL_LONG                      = 0x2F
+    THREE_D_INFORMATION_DATA                       = 0x3D
+    MANUFACTURER_SPECIFIC_DATA                     = 0xFF
+
+    AD_TYPE_NAMES = {
+        FLAGS:                                          'FLAGS',
+        INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:  'INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS',
+        COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:    'COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS',
+        INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:  'INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS',
+        COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:    'COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS',
+        INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 'INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS',
+        COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:   'COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS',
+        SHORTENED_LOCAL_NAME:                           'SHORTENED_LOCAL_NAME',
+        COMPLETE_LOCAL_NAME:                            'COMPLETE_LOCAL_NAME',
+        TX_POWER_LEVEL:                                 'TX_POWER_LEVEL',
+        CLASS_OF_DEVICE:                                'CLASS_OF_DEVICE',
+        SIMPLE_PAIRING_HASH_C:                          'SIMPLE_PAIRING_HASH_C',
+        SIMPLE_PAIRING_HASH_C_192:                      'SIMPLE_PAIRING_HASH_C_192',
+        SIMPLE_PAIRING_RANDOMIZER_R:                    'SIMPLE_PAIRING_RANDOMIZER_R',
+        SIMPLE_PAIRING_RANDOMIZER_R_192:                'SIMPLE_PAIRING_RANDOMIZER_R_192',
+        DEVICE_ID:                                      'DEVICE_ID',
+        SECURITY_MANAGER_TK_VALUE:                      'SECURITY_MANAGER_TK_VALUE',
+        SECURITY_MANAGER_OUT_OF_BAND_FLAGS:             'SECURITY_MANAGER_OUT_OF_BAND_FLAGS',
+        PERIPHERAL_CONNECTION_INTERVAL_RANGE:           'PERIPHERAL_CONNECTION_INTERVAL_RANGE',
+        LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS:      'LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS',
+        LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS:     'LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS',
+        SERVICE_DATA:                                   'SERVICE_DATA',
+        SERVICE_DATA_16_BIT_UUID:                       'SERVICE_DATA_16_BIT_UUID',
+        PUBLIC_TARGET_ADDRESS:                          'PUBLIC_TARGET_ADDRESS',
+        RANDOM_TARGET_ADDRESS:                          'RANDOM_TARGET_ADDRESS',
+        APPEARANCE:                                     'APPEARANCE',
+        ADVERTISING_INTERVAL:                           'ADVERTISING_INTERVAL',
+        LE_BLUETOOTH_DEVICE_ADDRESS:                    'LE_BLUETOOTH_DEVICE_ADDRESS',
+        LE_ROLE:                                        'LE_ROLE',
+        SIMPLE_PAIRING_HASH_C_256:                      'SIMPLE_PAIRING_HASH_C_256',
+        SIMPLE_PAIRING_RANDOMIZER_R_256:                'SIMPLE_PAIRING_RANDOMIZER_R_256',
+        LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS:      'LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS',
+        SERVICE_DATA_32_BIT_UUID:                       'SERVICE_DATA_32_BIT_UUID',
+        SERVICE_DATA_128_BIT_UUID:                      'SERVICE_DATA_128_BIT_UUID',
+        LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE:       'LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE',
+        LE_SECURE_CONNECTIONS_RANDOM_VALUE:             'LE_SECURE_CONNECTIONS_RANDOM_VALUE',
+        URI:                                            'URI',
+        INDOOR_POSITIONING:                             'INDOOR_POSITIONING',
+        TRANSPORT_DISCOVERY_DATA:                       'TRANSPORT_DISCOVERY_DATA',
+        LE_SUPPORTED_FEATURES:                          'LE_SUPPORTED_FEATURES',
+        CHANNEL_MAP_UPDATE_INDICATION:                  'CHANNEL_MAP_UPDATE_INDICATION',
+        PB_ADV:                                         'PB_ADV',
+        MESH_MESSAGE:                                   'MESH_MESSAGE',
+        MESH_BEACON:                                    'MESH_BEACON',
+        BIGINFO:                                        'BIGINFO',
+        BROADCAST_CODE:                                 'BROADCAST_CODE',
+        RESOLVABLE_SET_IDENTIFIER:                      'RESOLVABLE_SET_IDENTIFIER',
+        ADVERTISING_INTERVAL_LONG:                      'ADVERTISING_INTERVAL_LONG',
+        THREE_D_INFORMATION_DATA:                       'THREE_D_INFORMATION_DATA',
+        MANUFACTURER_SPECIFIC_DATA:                     'MANUFACTURER_SPECIFIC_DATA'
+    }
+
+    LE_LIMITED_DISCOVERABLE_MODE_FLAG = 0x01
+    LE_GENERAL_DISCOVERABLE_MODE_FLAG = 0x02
+    BR_EDR_NOT_SUPPORTED_FLAG         = 0x04
+    BR_EDR_CONTROLLER_FLAG            = 0x08
+    BR_EDR_HOST_FLAG                  = 0x10
+
+    def __init__(self, ad_structures = []):
+        self.ad_structures = ad_structures[:]
+
+    @staticmethod
+    def from_bytes(data):
+        instance = AdvertisingData()
+        instance.append(data)
+        return instance
+
+    @staticmethod
+    def flags_to_string(flags, short=False):
+        flag_names = [
+            'LE Limited',
+            'LE General',
+            'No BR/EDR',
+            'BR/EDR C',
+            'BR/EDR H'
+        ] if short else [
+            'LE Limited Discoverable Mode',
+            'LE General Discoverable Mode',
+            'BR/EDR Not Supported',
+            'Simultaneous LE and BR/EDR (Controller)',
+            'Simultaneous LE and BR/EDR (Host)'
+        ]
+        return ','.join(bit_flags_to_strings(flags, flag_names))
+
+    @staticmethod
+    def uuid_list_to_objects(ad_data, uuid_size):
+        uuids = []
+        offset = 0
+        while (uuid_size * (offset + 1)) <= len(ad_data):
+            uuids.append(UUID.from_bytes(ad_data[offset:offset + uuid_size]))
+            offset += uuid_size
+        return uuids
+
+    @staticmethod
+    def uuid_list_to_string(ad_data, uuid_size):
+        return ', '.join([
+            str(uuid)
+            for uuid in AdvertisingData.uuid_list_to_objects(ad_data, uuid_size)
+        ])
+
+    @staticmethod
+    def ad_data_to_string(ad_type, ad_data):
+        if ad_type == AdvertisingData.FLAGS:
+            ad_type_str = 'Flags'
+            ad_data_str = AdvertisingData.flags_to_string(ad_data[0], short=True)
+        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
+            ad_type_str = 'Complete List of 16-bit Service Class UUIDs'
+            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2)
+        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
+            ad_type_str = 'Incomplete List of 16-bit Service Class UUIDs'
+            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2)
+        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
+            ad_type_str = 'Complete List of 32-bit Service Class UUIDs'
+            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4)
+        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
+            ad_type_str = 'Incomplete List of 32-bit Service Class UUIDs'
+            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4)
+        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
+            ad_type_str = 'Complete List of 128-bit Service Class UUIDs'
+            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16)
+        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
+            ad_type_str = 'Incomplete List of 128-bit Service Class UUIDs'
+            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16)
+        elif ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
+            ad_type_str = 'Service Data'
+            uuid = UUID.from_bytes(ad_data[:2])
+            ad_data_str = f'service={uuid}, data={ad_data[2:].hex()}'
+        elif ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
+            ad_type_str = 'Service Data'
+            uuid = UUID.from_bytes(ad_data[:4])
+            ad_data_str = f'service={uuid}, data={ad_data[4:].hex()}'
+        elif ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
+            ad_type_str = 'Service Data'
+            uuid = UUID.from_bytes(ad_data[:16])
+            ad_data_str = f'service={uuid}, data={ad_data[16:].hex()}'
+        elif ad_type == AdvertisingData.SHORTENED_LOCAL_NAME:
+            ad_type_str = 'Shortened Local Name'
+            ad_data_str = f'"{ad_data.decode("utf-8")}"'
+        elif ad_type == AdvertisingData.COMPLETE_LOCAL_NAME:
+            ad_type_str = 'Complete Local Name'
+            ad_data_str = f'"{ad_data.decode("utf-8")}"'
+        elif ad_type == AdvertisingData.TX_POWER_LEVEL:
+            ad_type_str = 'TX Power Level'
+            ad_data_str = str(ad_data[0])
+        elif ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA:
+            ad_type_str = 'Manufacturer Specific Data'
+            company_id = struct.unpack_from('<H', ad_data, 0)[0]
+            company_name = COMPANY_IDENTIFIERS.get(company_id, f'0x{company_id:04X}')
+            ad_data_str = f'company={company_name}, data={ad_data[2:].hex()}'
+        elif ad_type == AdvertisingData.APPEARANCE:
+            ad_type_str = 'Appearance'
+            ad_data_str = ad_data.hex()
+        else:
+            ad_type_str = AdvertisingData.AD_TYPE_NAMES.get(ad_type, f'0x{ad_type:02X}')
+            ad_data_str = ad_data.hex()
+
+        return f'[{ad_type_str}]: {ad_data_str}'
+
+    @staticmethod
+    def ad_data_to_object(ad_type, ad_data):
+        if ad_type in {
+            AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
+            AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
+        }:
+            return AdvertisingData.uuid_list_to_objects(ad_data, 2)
+        elif ad_type in {
+            AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
+            AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS
+        }:
+            return AdvertisingData.uuid_list_to_objects(ad_data, 4)
+        elif ad_type in {
+            AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
+            AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS
+        }:
+            return AdvertisingData.uuid_list_to_objects(ad_data, 16)
+        elif ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
+            return (UUID.from_bytes(ad_data[:2]), ad_data[2:])
+        elif ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
+            return (UUID.from_bytes(ad_data[:4]), ad_data[4:])
+        elif ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
+            return (UUID.from_bytes(ad_data[:16]), ad_data[16:])
+        elif ad_type in {
+            AdvertisingData.SHORTENED_LOCAL_NAME,
+            AdvertisingData.COMPLETE_LOCAL_NAME
+        }:
+            return ad_data.decode("utf-8")
+        elif ad_type == AdvertisingData.TX_POWER_LEVEL:
+            return ad_data[0]
+        elif ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA:
+            return (struct.unpack_from('<H', ad_data, 0)[0], ad_data[2:])
+        else:
+            return ad_data
+
+    def append(self, data):
+        offset = 0
+        while offset + 1 < len(data):
+            length = data[offset]
+            offset += 1
+            if length > 0:
+                ad_type = data[offset]
+                ad_data = data[offset + 1:offset + length]
+                self.ad_structures.append((ad_type, ad_data))
+            offset += length
+
+    def get(self, type_id, return_all=False, raw=True):
+        '''
+        Get Advertising Data Structure(s) with a given type
+
+        If return_all is True, returns a (possibly empty) list of matches,
+        else returns the first entry, or None if no structure matches.
+        '''
+        def process_ad_data(ad_data):
+            return ad_data if raw else self.ad_data_to_object(type_id, ad_data)
+
+        if return_all:
+            return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id]
+        else:
+            return next((process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id), None)
+
+    def __bytes__(self):
+        return b''.join([bytes([len(x[1]) + 1, x[0]]) + x[1] for x in self.ad_structures])
+
+    def to_string(self, separator=', '):
+        return separator.join([AdvertisingData.ad_data_to_string(x[0], x[1]) for x in self.ad_structures])
+
+    def __str__(self):
+        return self.to_string()
+
+
+# -----------------------------------------------------------------------------
+# Connection Parameters
+# -----------------------------------------------------------------------------
+class ConnectionParameters:
+    def __init__(self, connection_interval, connection_latency, supervision_timeout):
+        self.connection_interval = connection_interval
+        self.connection_latency  = connection_latency
+        self.supervision_timeout = supervision_timeout
+
+    def __str__(self):
+        return f'ConnectionParameters(connection_interval={self.connection_interval}, connection_latency={self.connection_latency}, supervision_timeout={self.supervision_timeout}'
+
+
+# -----------------------------------------------------------------------------
+# Connection PHY
+# -----------------------------------------------------------------------------
+class ConnectionPHY:
+    def __init__(self, tx_phy, rx_phy):
+        self.tx_phy = tx_phy
+        self.rx_phy = rx_phy
+
+    def __str__(self):
+        return f'ConnectionPHY(tx_phy={self.tx_phy}, rx_phy={self.rx_phy})'
diff --git a/bumble/crypto.py b/bumble/crypto.py
new file mode 100644
index 0000000..4f13476
--- /dev/null
+++ b/bumble/crypto.py
@@ -0,0 +1,243 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Crypto support
+#
+# See Bluetooth spec Vol 3, Part H - 2.2 CRYPTOGRAPHIC TOOLBOX
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import operator
+import platform
+if platform.system() != 'Emscripten':
+    import secrets
+    from cryptography.hazmat.primitives.ciphers import (
+        Cipher,
+        algorithms,
+        modes
+    )
+    from cryptography.hazmat.primitives.asymmetric.ec import (
+        generate_private_key,
+        ECDH,
+        EllipticCurvePublicNumbers,
+        EllipticCurvePrivateNumbers,
+        SECP256R1
+    )
+    from cryptography.hazmat.primitives import cmac
+else:
+    # TODO: implement stubs
+    pass
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Classes
+# -----------------------------------------------------------------------------
+class EccKey:
+    def __init__(self, private_key):
+        self.private_key = private_key
+
+    @classmethod
+    def generate(cls):
+        private_key = generate_private_key(SECP256R1())
+        return cls(private_key)
+
+    @classmethod
+    def from_private_key_bytes(cls, d_bytes, x_bytes, y_bytes):
+        d = int.from_bytes(d_bytes, byteorder='big', signed=False)
+        x = int.from_bytes(x_bytes, byteorder='big', signed=False)
+        y = int.from_bytes(y_bytes, byteorder='big', signed=False)
+        private_key = EllipticCurvePrivateNumbers(d, EllipticCurvePublicNumbers(x, y, SECP256R1())).private_key()
+        return cls(private_key)
+
+    @property
+    def x(self):
+        return self.private_key.public_key().public_numbers().x.to_bytes(32, byteorder='big')
+
+    @property
+    def y(self):
+        return self.private_key.public_key().public_numbers().y.to_bytes(32, byteorder='big')
+
+    def dh(self, public_key_x, public_key_y):
+        x = int.from_bytes(public_key_x, byteorder='big', signed=False)
+        y = int.from_bytes(public_key_y, byteorder='big', signed=False)
+        public_key = EllipticCurvePublicNumbers(x, y, SECP256R1()).public_key()
+        shared_key = self.private_key.exchange(ECDH(), public_key)
+
+        return shared_key
+
+
+# -----------------------------------------------------------------------------
+# Functions
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+def xor(x, y):
+    assert(len(x) == len(y))
+    return bytes(map(operator.xor, x, y))
+
+
+# -----------------------------------------------------------------------------
+def r():
+    '''
+    Generate 16 bytes of random data
+    '''
+    return secrets.token_bytes(16)
+
+
+# -----------------------------------------------------------------------------
+def e(key, data):
+    '''
+    AES-128 ECB, expecting byte-swapped inputs and producing a byte-swapped output.
+
+    See Bluetooth spec Vol 3, Part H - 2.2.1 Security function e
+    '''
+
+    cipher = Cipher(algorithms.AES(bytes(reversed(key))), modes.ECB())
+    encryptor = cipher.encryptor()
+    return bytes(reversed(encryptor.update(bytes(reversed(data)))))
+
+
+# -----------------------------------------------------------------------------
+def ah(k, r):
+    '''
+    See Bluetooth spec Vol 3, Part H - 2.2.2 Random Address Hash function ah
+    '''
+
+    padding = bytes(13)
+    r_prime = r + padding
+    return e(k, r_prime)[0:3]
+
+
+# -----------------------------------------------------------------------------
+def c1(k, r, preq, pres, iat, rat, ia, ra):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.3 Confirm value generation function c1 for LE Legacy Pairing
+    '''
+
+    p1 = bytes([iat, rat]) + preq + pres
+    p2 = ra + ia + bytes([0, 0, 0, 0])
+    return e(k, xor(e(k, xor(r, p1)), p2))
+
+
+# -----------------------------------------------------------------------------
+def s1(k, r1, r2):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.4 Key generation function s1 for LE Legacy Pairing
+    '''
+
+    return e(k, r2[0:8] + r1[0:8])
+
+
+# -----------------------------------------------------------------------------
+def aes_cmac(m, k):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.5 FunctionAES-CMAC
+
+    NOTE: the input and output of this internal function are in big-endian byte order
+    '''
+    mac = cmac.CMAC(algorithms.AES(k))
+    mac.update(m)
+    return mac.finalize()
+
+
+# -----------------------------------------------------------------------------
+def f4(u, v, x, z):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.6 LE Secure Connections Confirm Value Generation Function f4
+    '''
+    return bytes(reversed(aes_cmac(bytes(reversed(u)) + bytes(reversed(v)) + z, bytes(reversed(x)))))
+
+
+# -----------------------------------------------------------------------------
+def f5(w, n1, n2, a1, a2):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.7 LE Secure Connections Key Generation Function f5
+
+    NOTE: this returns a tuple: (MacKey, LTK) in little-endian byte order
+    '''
+    salt = bytes.fromhex('6C888391AAF5A53860370BDB5A6083BE')
+    t = aes_cmac(bytes(reversed(w)), salt)
+    key_id = bytes([0x62, 0x74, 0x6c, 0x65])
+    return (
+        bytes(reversed(aes_cmac(
+            bytes([0]) +
+            key_id +
+            bytes(reversed(n1)) +
+            bytes(reversed(n2)) +
+            bytes(reversed(a1)) +
+            bytes(reversed(a2)) +
+            bytes([1, 0]),
+            t
+        ))),
+        bytes(reversed(aes_cmac(
+            bytes([1]) +
+            key_id +
+            bytes(reversed(n1)) +
+            bytes(reversed(n2)) +
+            bytes(reversed(a1)) +
+            bytes(reversed(a2)) +
+            bytes([1, 0]),
+            t
+        )))
+    )
+
+
+# -----------------------------------------------------------------------------
+def f6(w, n1, n2, r, io_cap, a1, a2):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.8 LE Secure Connections Check Value Generation Function f6
+    '''
+    return bytes(reversed(aes_cmac(
+        bytes(reversed(n1)) +
+        bytes(reversed(n2)) +
+        bytes(reversed(r)) +
+        bytes(reversed(io_cap)) +
+        bytes(reversed(a1)) +
+        bytes(reversed(a2)),
+        bytes(reversed(w))
+    )))
+
+
+# -----------------------------------------------------------------------------
+def g2(u, v, x, y):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.9 LE Secure Connections Numeric Comparison Value Generation Function g2
+    '''
+    return int.from_bytes(
+        aes_cmac(bytes(reversed(u)) + bytes(reversed(v)) + bytes(reversed(y)), bytes(reversed(x)))[-4:],
+        byteorder='big'
+    )
+
+# -----------------------------------------------------------------------------
+def h6(w, key_id):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.10 Link key conversion function h6
+    '''
+    return aes_cmac(key_id, w)
+
+# -----------------------------------------------------------------------------
+def h7(salt, w):
+    '''
+    See Bluetooth spec, Vol 3, Part H - 2.2.11 Link key conversion function h7
+    '''
+    return aes_cmac(w, salt)
diff --git a/bumble/device.py b/bumble/device.py
new file mode 100644
index 0000000..97ead5e
--- /dev/null
+++ b/bumble/device.py
@@ -0,0 +1,1475 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import json
+import asyncio
+import logging
+
+from .hci import *
+from .host import Host
+from .gatt import *
+from .gap import GenericAccessService
+from .core import AdvertisingData, BT_CENTRAL_ROLE, BT_PERIPHERAL_ROLE
+from .utils import AsyncRunner, CompositeEventEmitter, setup_event_forwarding, composite_listener
+from . import gatt_client
+from . import gatt_server
+from . import smp
+from . import sdp
+from . import l2cap
+from . import keys
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+DEVICE_DEFAULT_ADDRESS              = '00:00:00:00:00:00'
+DEVICE_DEFAULT_ADVERTISING_INTERVAL = 1000  # ms
+DEVICE_DEFAULT_ADVERTISING_DATA     = ''
+DEVICE_DEFAULT_NAME                 = 'Bumble'
+DEVICE_DEFAULT_INQUIRY_LENGTH       = 8  # 10.24 seconds
+DEVICE_DEFAULT_CLASS_OF_DEVICE      = 0
+DEVICE_DEFAULT_SCAN_RESPONSE_DATA   = b''
+DEVICE_DEFAULT_DATA_LENGTH          = (27, 328, 27, 328)
+DEVICE_DEFAULT_SCAN_INTERVAL        = 60  # ms
+DEVICE_DEFAULT_SCAN_WINDOW          = 60  # ms
+DEVICE_MIN_SCAN_INTERVAL            = 25
+DEVICE_MAX_SCAN_INTERVAL            = 10240
+DEVICE_MIN_SCAN_WINDOW              = 25
+DEVICE_MAX_SCAN_WINDOW              = 10240
+
+# -----------------------------------------------------------------------------
+# Classes
+# -----------------------------------------------------------------------------
+
+
+# -----------------------------------------------------------------------------
+class AdvertisementDataAccumulator:
+    def __init__(self):
+        self.advertising_data = AdvertisingData()
+        self.last_advertisement_type = None
+        self.connectable = False
+        self.flushable = False
+
+    def update(self, data, advertisement_type):
+        if advertisement_type == HCI_LE_Advertising_Report_Event.SCAN_RSP:
+            if self.last_advertisement_type != HCI_LE_Advertising_Report_Event.SCAN_RSP:
+                self.advertising_data.append(data)
+                self.flushable = True
+        else:
+            self.advertising_data = AdvertisingData.from_bytes(data)
+            self.flushable = self.last_advertisement_type != HCI_LE_Advertising_Report_Event.SCAN_RSP
+
+        if advertisement_type == HCI_LE_Advertising_Report_Event.ADV_IND or advertisement_type == HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND:
+            self.connectable = True
+        elif advertisement_type == HCI_LE_Advertising_Report_Event.ADV_SCAN_IND or advertisement_type == HCI_LE_Advertising_Report_Event.ADV_NONCONN_IND:
+            self.connectable = False
+
+        self.last_advertisement_type = advertisement_type
+
+
+# -----------------------------------------------------------------------------
+class Peer:
+    def __init__(self, connection):
+        self.connection = connection
+
+        # Create a GATT client for the connection
+        self.gatt_client = gatt_client.Client(connection)
+        connection.gatt_client = self.gatt_client
+
+    @property
+    def services(self):
+        return self.gatt_client.services
+
+    async def request_mtu(self, mtu):
+        return await self.gatt_client.request_mtu(mtu)
+
+    async def discover_service(self, uuid):
+        return await self.gatt_client.discover_service(uuid)
+
+    async def discover_services(self, uuids = []):
+        return await self.gatt_client.discover_services(uuids)
+
+    async def discover_included_services(self, service):
+        return await self.gatt_client.discover_included_services(service)
+
+    async def discover_characteristics(self, uuids = [], service = None):
+        return await self.gatt_client.discover_characteristics(uuids = uuids, service = service)
+
+    async def discover_descriptors(self, characteristic = None, start_handle = None, end_handle = None):
+        return await self.gatt_client.discover_descriptors(characteristic, start_handle, end_handle)
+
+    async def discover_attributes(self):
+        return await self.gatt_client.discover_attributes()
+
+    async def subscribe(self, characteristic, subscriber=None):
+        return await self.gatt_client.subscribe(characteristic, subscriber)
+
+    async def read_value(self, attribute):
+        return await self.gatt_client.read_value(attribute)
+
+    async def write_value(self, attribute, value, with_response=False):
+        return await self.gatt_client.write_value(attribute, value, with_response)
+
+    async def read_characteristics_by_uuid(self, uuid, service=None):
+        return await self.gatt_client.read_characteristics_by_uuid(uuid, service)
+
+    def get_services_by_uuid(self, uuid):
+        return self.gatt_client.get_services_by_uuid(uuid)
+
+    def get_characteristics_by_uuid(self, uuid, service = None):
+        return self.gatt_client.get_characteristics_by_uuid(uuid, service)
+
+    def create_service_proxy(self, proxy_class):
+        return proxy_class.from_client(self.gatt_client)
+
+    async def discover_service_and_create_proxy(self, proxy_class):
+        # Discover the first matching service and its characteristics
+        services = await self.discover_service(proxy_class.SERVICE_CLASS.UUID)
+        if services:
+            service = services[0]
+            await service.discover_characteristics()
+            return self.create_service_proxy(proxy_class)
+
+    # [Classic only]
+    async def request_name(self):
+        return await self.connection.request_remote_name()
+
+    def __str__(self):
+        return f'{self.connection.peer_address} as {self.connection.role_name}'
+
+
+# -----------------------------------------------------------------------------
+class Connection(CompositeEventEmitter):
+    @composite_listener
+    class Listener:
+        def on_disconnection(self, reason):
+            pass
+
+        def on_connection_parameters_update(self):
+            pass
+
+        def on_connection_parameters_update_failure(self, error):
+            pass
+
+        def on_connection_phy_update(self):
+            pass
+
+        def on_connection_phy_update_failure(self, error):
+            pass
+
+        def on_connection_att_mtu_update(self):
+            pass
+
+        def on_connection_encryption_change(self):
+            pass
+
+        def on_connection_encryption_key_refresh(self):
+            pass
+
+    def __init__(self, device, handle, transport, peer_address, peer_resolvable_address, role, parameters):
+        super().__init__()
+        self.device                  = device
+        self.handle                  = handle
+        self.transport               = transport
+        self.peer_address            = peer_address
+        self.peer_resolvable_address = peer_resolvable_address
+        self.peer_name               = None  # Classic only
+        self.role                    = role
+        self.parameters              = parameters
+        self.encryption              = 0
+        self.authenticated           = False
+        self.phy                     = ConnectionPHY(HCI_LE_1M_PHY, HCI_LE_1M_PHY)
+        self.att_mtu                 = ATT_DEFAULT_MTU
+        self.data_length             = DEVICE_DEFAULT_DATA_LENGTH
+        self.gatt_client             = None  # Per-connection client
+        self.gatt_server             = device.gatt_server  # By default, use the device's shared server
+
+    @property
+    def role_name(self):
+        return 'CENTRAL' if self.role == BT_CENTRAL_ROLE else 'PERIPHERAL'
+
+    @property
+    def is_encrypted(self):
+        return self.encryption != 0
+
+    def send_l2cap_pdu(self, cid, pdu):
+        self.device.send_l2cap_pdu(self.handle, cid, pdu)
+
+    def create_l2cap_connector(self, psm):
+        return self.device.create_l2cap_connector(self, psm)
+
+    async def disconnect(self, reason = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR):
+        return await self.device.disconnect(self, reason)
+
+    async def pair(self):
+        return await self.device.pair(self)
+
+    def request_pairing(self):
+        return self.device.request_pairing(self)
+
+    # [Classic only]
+    async def authenticate(self):
+        return await self.device.authenticate(self)
+
+    async def encrypt(self):
+        return await self.device.encrypt(self)
+
+    async def update_parameters(
+        self,
+        conn_interval_min,
+        conn_interval_max,
+        conn_latency,
+        supervision_timeout
+    ):
+        return await self.device.update_connection_parameters(
+            self,
+            conn_interval_min,
+            conn_interval_max,
+            conn_latency,
+            supervision_timeout
+        )
+
+    # [Classic only]
+    async def request_remote_name(self):
+        return await self.device.request_remote_name(self)
+
+    def __str__(self):
+        return f'Connection(handle=0x{self.handle:04X}, role={self.role_name}, address={self.peer_address})'
+
+
+# -----------------------------------------------------------------------------
+class DeviceConfiguration:
+    def __init__(self):
+        # Setup defaults
+        self.name                     = DEVICE_DEFAULT_NAME
+        self.address                  = DEVICE_DEFAULT_ADDRESS
+        self.class_of_device          = DEVICE_DEFAULT_CLASS_OF_DEVICE
+        self.scan_response_data       = DEVICE_DEFAULT_SCAN_RESPONSE_DATA
+        self.advertising_interval_min = DEVICE_DEFAULT_ADVERTISING_INTERVAL
+        self.advertising_interval_max = DEVICE_DEFAULT_ADVERTISING_INTERVAL
+        self.le_enabled               = True
+        # LE host enable 2nd parameter
+        self.le_simultaneous_enabled  = True
+        self.classic_sc_enabled       = True
+        self.classic_ssp_enabled      = True
+        self.advertising_data = bytes(
+            AdvertisingData([(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8'))])
+        )
+        self.irk      = bytes(16)  # This really must be changed for any level of security
+        self.keystore = None
+
+    def load_from_dict(self, config):
+        # Load simple properties
+        self.name = config.get('name', self.name)
+        self.address = Address(config.get('address', self.address))
+        self.class_of_device = config.get('class_of_device', self.class_of_device)
+        self.advertising_interval_min = config.get('advertising_interval', self.advertising_interval_min)
+        self.advertising_interval_max = self.advertising_interval_min
+        self.keystore                 = config.get('keystore')
+        self.le_enabled               = config.get('le_enabled', self.le_enabled)
+        self.le_simultaneous_enabled  = config.get('le_simultaneous_enabled', self.le_simultaneous_enabled)
+        self.classic_sc_enabled       = config.get('classic_sc_enabled', self.classic_sc_enabled)
+        self.classic_ssp_enabled      = config.get('classic_ssp_enabled', self.classic_ssp_enabled)
+
+        # Load or synthesize an IRK
+        irk = config.get('irk')
+        if irk:
+            self.irk = bytes.fromhex(irk)
+        else:
+            # Construct an IRK from the address bytes
+            # NOTE: this is not secure, but will always give the same IRK for the same address
+            address_bytes = bytes(self.address)
+            self.irk = (address_bytes * 3)[:16]
+
+        # Load advertising data
+        advertising_data = config.get('advertising_data')
+        if advertising_data:
+            self.advertising_data = bytes.fromhex(advertising_data)
+
+    def load_from_file(self, filename):
+        with open(filename, 'r') as file:
+            self.load_from_dict(json.load(file))
+
+# -----------------------------------------------------------------------------
+# Decorators used with the following Device class
+# (we define them outside of the Device class, because defining decorators
+#  within a class requires unnecessarily complicated acrobatics)
+# -----------------------------------------------------------------------------
+
+# Decorator that converts the first argument from a connection handle to a connection
+def with_connection_from_handle(function):
+    @functools.wraps(function)
+    def wrapper(self, connection_handle, *args, **kwargs):
+        if (connection := self.lookup_connection(connection_handle)) is None:
+            raise ValueError('no connection for handle')
+        return function(self, connection, *args, **kwargs)
+    return wrapper
+
+
+# Decorator that converts the first argument from a bluetooth address to a connection
+def with_connection_from_address(function):
+    @functools.wraps(function)
+    def wrapper(self, address, *args, **kwargs):
+        for connection in self.connections.values():
+            if connection.peer_address == address:
+                return function(self, connection, *args, **kwargs)
+        raise ValueError('no connection for address')
+    return wrapper
+
+
+# Decorator that adds a method to the list of event handlers for host events.
+# This assumes that the method name starts with `on_`
+def host_event_handler(function):
+    device_host_event_handlers.append(function.__name__[3:])
+    return function
+
+
+# List of host event handlers for the Device class.
+# (we define this list outside the class, because referencing a class in method
+#  decorators is not straightforward)
+device_host_event_handlers = []
+
+
+# -----------------------------------------------------------------------------
+class Device(CompositeEventEmitter):
+
+    @composite_listener
+    class Listener:
+        def on_advertisement(self, address, data, rssi, advertisement_type):
+            pass
+
+        def on_inquiry_result(self, address, class_of_device, data, rssi):
+            pass
+
+        def on_connection(self, connection):
+            pass
+
+        def on_connection_failure(self, error):
+            pass
+
+        def on_characteristic_subscription(self, connection, characteristic, notify_enabled, indicate_enabled):
+            pass
+
+    @classmethod
+    def with_hci(cls, name, address, hci_source, hci_sink):
+        '''
+        Create a Device instance with a Host configured to communicate with a controller
+        through an HCI source/sink
+        '''
+        host = Host(controller_source = hci_source, controller_sink = hci_sink)
+        return cls(name = name, address = address, host = host)
+
+    @classmethod
+    def from_config_file(cls, filename):
+        config = DeviceConfiguration()
+        config.load_from_file(filename)
+        return cls(config=config)
+
+    @classmethod
+    def from_config_file_with_hci(cls, filename, hci_source, hci_sink):
+        config = DeviceConfiguration()
+        config.load_from_file(filename)
+        host = Host(controller_source = hci_source, controller_sink = hci_sink)
+        return cls(config = config, host = host)
+
+    def __init__(self, name = None, address = None, config = None, host = None, generic_access_service = True):
+        super().__init__()
+
+        self._host                    = None
+        self.powered_on               = False
+        self.advertising              = False
+        self.auto_restart_advertising = False
+        self.command_timeout          = 10  # seconds
+        self.gatt_server              = gatt_server.Server(self)
+        self.sdp_server               = sdp.Server(self)
+        self.l2cap_channel_manager    = l2cap.ChannelManager()
+        self.advertisement_data       = {}
+        self.scanning                 = False
+        self.discovering              = False
+        self.connecting               = False
+        self.disconnecting            = False
+        self.connections              = {}  # Connections, by connection handle
+        self.classic_enabled          = False
+        self.discoverable             = False
+        self.connectable              = False
+        self.inquiry_response         = None
+        self.address_resolver         = None
+
+        # Use the initial config or a default
+        self.public_address = Address('00:00:00:00:00:00')
+        if config is None:
+            config = DeviceConfiguration()
+        self.name                     = config.name
+        self.random_address           = config.address
+        self.class_of_device          = config.class_of_device
+        self.scan_response_data       = config.scan_response_data
+        self.advertising_data         = config.advertising_data
+        self.advertising_interval_min = config.advertising_interval_min
+        self.advertising_interval_max = config.advertising_interval_max
+        self.keystore                 = keys.KeyStore.create_for_device(config)
+        self.irk                      = config.irk
+        self.le_enabled               = config.le_enabled
+        self.le_simultaneous_enabled  = config.le_simultaneous_enabled
+        self.classic_ssp_enabled      = config.classic_ssp_enabled
+        self.classic_sc_enabled       = config.classic_sc_enabled
+
+        # If a name is passed, override the name from the config
+        if name:
+            self.name = name
+
+        # If an address is passed, override the address from the config
+        if address:
+            if type(address) is str:
+                address = Address(address)
+            self.random_address = address
+
+        # Setup SMP
+        # TODO: allow using a public address
+        self.smp_manager = smp.Manager(self, self.random_address)
+
+        # Register the SDP server with the L2CAP Channel Manager
+        self.sdp_server.register(self.l2cap_channel_manager)
+
+        # Add a GAP Service if requested
+        if generic_access_service:
+            self.gatt_server.add_service(GenericAccessService(self.name))
+
+        # Forward some events
+        setup_event_forwarding(self.gatt_server, self, 'characteristic_subscription')
+
+        # Set the initial host
+        self.host = host
+
+    @property
+    def host(self):
+        return self._host
+
+    @host.setter
+    def host(self, host):
+        # Unsubscribe from events from the current host
+        if self._host:
+            for event_name in device_host_event_handlers:
+                self._host.remove_listener(event_name, getattr(self, f'on_{event_name}'))
+
+        # Subscribe to events from the new host
+        if host:
+            for event_name in device_host_event_handlers:
+                host.on(event_name, getattr(self, f'on_{event_name}'))
+
+        # Update the references to the new host
+        self._host                      = host
+        self.l2cap_channel_manager.host = host
+
+        # Set providers for the new host
+        if host:
+            host.long_term_key_provider = self.get_long_term_key
+            host.link_key_provider      = self.get_link_key
+
+    @property
+    def sdp_service_records(self):
+        return self.sdp_server.service_records
+
+    @sdp_service_records.setter
+    def sdp_service_records(self, service_records):
+        self.sdp_server.service_records = service_records
+
+    def lookup_connection(self, connection_handle):
+        if connection := self.connections.get(connection_handle):
+            return connection
+
+    def find_connection_by_bd_addr(self, bd_addr, transport=None):
+        for connection in self.connections.values():
+            if connection.peer_address.get_bytes() == bd_addr.get_bytes():
+                if transport is None or connection.transport == transport:
+                    return connection
+
+    def register_l2cap_server(self, psm, server):
+        self.l2cap_channel_manager.register_server(psm, server)
+
+    def create_l2cap_connector(self, connection, psm):
+        return lambda: self.l2cap_channel_manager.connect(connection, psm)
+
+    def create_l2cap_registrar(self, psm):
+        return lambda handler: self.register_l2cap_server(psm, handler)
+
+    def send_l2cap_pdu(self, connection_handle, cid, pdu):
+        self.host.send_l2cap_pdu(connection_handle, cid, pdu)
+
+    async def send_command(self, command):
+        try:
+            return await asyncio.wait_for(self.host.send_command(command), self.command_timeout)
+        except asyncio.TimeoutError:
+            logger.warning('!!! Command timed out')
+
+    async def power_on(self):
+        # Reset the controller
+        await self.host.reset()
+
+        response = await self.send_command(HCI_Read_BD_ADDR_Command())
+        if response.return_parameters.status == HCI_SUCCESS:
+            logger.debug(color(f'BD_ADDR: {response.return_parameters.bd_addr}', 'yellow'))
+            self.public_address = response.return_parameters.bd_addr
+
+
+        await self.send_command(HCI_Write_LE_Host_Support_Command(
+            le_supported_host = int(self.le_enabled),
+            simultaneous_le_host = int(self.le_simultaneous_enabled),
+        ))
+        if self.le_enabled:
+            # Set the controller address
+            await self.send_command(HCI_LE_Set_Random_Address_Command(
+                random_address = self.random_address
+            ))
+
+            # Load the address resolving list
+            if self.keystore:
+                await self.send_command(HCI_LE_Clear_Resolving_List_Command())
+
+                resolving_keys = await self.keystore.get_resolving_keys()
+                for (irk, address) in resolving_keys:
+                    await self.send_command(
+                        HCI_LE_Add_Device_To_Resolving_List_Command(
+                            peer_identity_address_type = address.address_type,
+                            peer_identity_address      = address,
+                            peer_irk                   = irk,
+                            local_irk                  = self.irk
+                        )
+                    )
+
+                # Enable address resolution
+                # await self.send_command(
+                #     HCI_LE_Set_Address_Resolution_Enable_Command(address_resolution_enable=1)
+                # )
+
+                # Create a host-side address resolver
+                self.address_resolver = smp.AddressResolver(resolving_keys)
+
+        if self.classic_enabled:
+            await self.send_command(
+                HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8'))
+            )
+            await self.send_command(
+                HCI_Write_Class_Of_Device_Command(class_of_device = self.class_of_device)
+            )
+            await self.send_command(
+                HCI_Write_Simple_Pairing_Mode_Command(
+                    simple_pairing_mode=int(self.classic_ssp_enabled))
+            )
+            await self.send_command(
+                HCI_Write_Secure_Connections_Host_Support_Command(
+                    secure_connections_host_support=int(self.classic_sc_enabled))
+            )
+
+        # Let the SMP manager know about the address
+        # TODO: allow using a public address
+        self.smp_manager.address = self.random_address
+
+        # Done
+        self.powered_on = True
+
+    async def start_advertising(self, auto_restart=False):
+        self.auto_restart_advertising = auto_restart
+
+        # If we're advertising, stop first
+        if self.advertising:
+            await self.stop_advertising()
+
+        # Set/update the advertising data
+        await self.send_command(HCI_LE_Set_Advertising_Data_Command(
+            advertising_data = self.advertising_data
+        ))
+
+        # Set/update the scan response data
+        await self.send_command(HCI_LE_Set_Scan_Response_Data_Command(
+            scan_response_data = self.scan_response_data
+        ))
+
+        # Set the advertising parameters
+        await self.send_command(HCI_LE_Set_Advertising_Parameters_Command(
+            # TODO: use real values, not fixed ones
+            advertising_interval_min  = self.advertising_interval_min,
+            advertising_interval_max  = self.advertising_interval_max,
+            advertising_type          = HCI_LE_Set_Advertising_Parameters_Command.ADV_IND,
+            own_address_type          = Address.RANDOM_DEVICE_ADDRESS,  # TODO: allow using the public address
+            peer_address_type         = Address.PUBLIC_DEVICE_ADDRESS,
+            peer_address              = Address('00:00:00:00:00:00'),
+            advertising_channel_map   = 7,
+            advertising_filter_policy = 0
+        ))
+
+        # Enable advertising
+        await self.send_command(HCI_LE_Set_Advertising_Enable_Command(
+            advertising_enable = 1
+        ))
+
+        self.advertising = True
+
+    async def stop_advertising(self):
+        # Disable advertising
+        if self.advertising:
+            await self.send_command(HCI_LE_Set_Advertising_Enable_Command(
+                advertising_enable = 0
+            ))
+
+            self.advertising = False
+
+    @property
+    def is_advertising(self):
+        return self.advertising
+
+    async def start_scanning(
+        self,
+        active=True,
+        scan_interval=DEVICE_DEFAULT_SCAN_INTERVAL,  # Scan interval in ms
+        scan_window=DEVICE_DEFAULT_SCAN_WINDOW,      # Scan window in ms
+        own_address_type=Address.RANDOM_DEVICE_ADDRESS,
+        filter_duplicates=False
+    ):
+        # Check that the arguments are legal
+        if scan_interval < scan_window:
+            raise ValueError('scan_interval must be >= scan_window')
+        if scan_interval < DEVICE_MIN_SCAN_INTERVAL or scan_interval > DEVICE_MAX_SCAN_INTERVAL:
+            raise ValueError('scan_interval out of range')
+        if scan_window < DEVICE_MIN_SCAN_WINDOW or scan_window > DEVICE_MAX_SCAN_WINDOW:
+            raise ValueError('scan_interval out of range')
+
+        # Set the scanning parameters
+        scan_type = HCI_LE_Set_Scan_Parameters_Command.ACTIVE_SCANNING if active else HCI_LE_Set_Scan_Parameters_Command.PASSIVE_SCANNING
+        await self.send_command(HCI_LE_Set_Scan_Parameters_Command(
+            le_scan_type           = scan_type,
+            le_scan_interval       = int(scan_window / 0.625),
+            le_scan_window         = int(scan_window / 0.625),
+            own_address_type       = own_address_type,
+            scanning_filter_policy = HCI_LE_Set_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY
+        ))
+
+        # Enable scanning
+        await self.send_command(HCI_LE_Set_Scan_Enable_Command(
+            le_scan_enable    = 1,
+            filter_duplicates = 1 if filter_duplicates else 0
+        ))
+        self.scanning = True
+
+    async def stop_scanning(self):
+        await self.send_command(HCI_LE_Set_Scan_Enable_Command(
+            le_scan_enable    = 0,
+            filter_duplicates = 0
+        ))
+        self.scanning = False
+
+    @property
+    def is_scanning(self):
+        return self.scanning
+
+    @host_event_handler
+    def on_advertising_report(self, address, data, rssi, advertisement_type):
+        if not (accumulator := self.advertisement_data.get(address)):
+            accumulator = AdvertisementDataAccumulator()
+            self.advertisement_data[address] = accumulator
+        accumulator.update(data, advertisement_type)
+        if accumulator.flushable:
+            self.emit(
+                'advertisement',
+                address,
+                accumulator.advertising_data,
+                rssi,
+                accumulator.connectable
+            )
+
+    async def start_discovery(self):
+        await self.host.send_command(HCI_Write_Inquiry_Mode_Command(inquiry_mode=HCI_EXTENDED_INQUIRY_MODE))
+
+        response = await self.send_command(HCI_Inquiry_Command(
+            lap            = HCI_GENERAL_INQUIRY_LAP,
+            inquiry_length = DEVICE_DEFAULT_INQUIRY_LENGTH,
+            num_responses  = 0  # Unlimited number of responses.
+        ))
+        if response.status != HCI_Command_Status_Event.PENDING:
+            self.discovering = False
+            raise RuntimeError(f'HCI_Inquiry command failed: {HCI_Constant.status_name(response.status)} ({response.status})')
+
+        self.discovering = True
+
+    async def stop_discovery(self):
+        await self.send_command(HCI_Inquiry_Cancel_Command())
+        self.discovering = False
+
+    @host_event_handler
+    def on_inquiry_result(self, address, class_of_device, data, rssi):
+        self.emit(
+            'inquiry_result',
+            address,
+            class_of_device,
+            AdvertisingData.from_bytes(data),
+            rssi
+        )
+
+    async def set_scan_enable(self, inquiry_scan_enabled, page_scan_enabled):
+        if inquiry_scan_enabled and page_scan_enabled:
+            scan_enable = 0x03
+        elif page_scan_enabled:
+            scan_enable = 0x02
+        elif inquiry_scan_enabled:
+            scan_enable = 0x01
+        else:
+            scan_enable = 0x00
+
+        return await self.send_command(HCI_Write_Scan_Enable_Command(scan_enable = scan_enable))
+
+    async def set_discoverable(self, discoverable=True):
+        self.discoverable = discoverable
+        if self.classic_enabled:
+            # Synthesize an inquiry response if none is set already
+            if self.inquiry_response is None:
+                self.inquiry_response = bytes(
+                    AdvertisingData([
+                        (AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8'))
+                    ])
+                )
+
+            # Update the controller
+            await self.host.send_command(
+                HCI_Write_Extended_Inquiry_Response_Command(
+                    fec_required              = 0,
+                    extended_inquiry_response = self.inquiry_response
+                )
+            )
+            await self.set_scan_enable(
+                inquiry_scan_enabled = self.discoverable,
+                page_scan_enabled    = self.connectable
+            )
+
+    async def set_connectable(self, connectable=True):
+        self.connectable = connectable
+        if self.classic_enabled:
+            await self.set_scan_enable(
+                inquiry_scan_enabled = self.discoverable,
+                page_scan_enabled    = self.connectable
+            )
+
+    async def connect(self, peer_address, transport=BT_LE_TRANSPORT):
+        '''
+        Request a connection to a peer.
+        This method cannot be called if there is already a pending connection.
+        '''
+
+        # Adjust the transport automatically if we need to
+        if transport == BT_LE_TRANSPORT and not self.le_enabled:
+            transport = BT_BR_EDR_TRANSPORT
+        elif transport == BT_BR_EDR_TRANSPORT and not self.classic_enabled:
+            transport = BT_LE_TRANSPORT
+
+        # Check that there isn't already a pending connection
+        if self.is_connecting:
+            raise InvalidStateError('connection already pending')
+
+        if type(peer_address) is str:
+            try:
+                peer_address = Address(peer_address)
+            except ValueError:
+                # If the address is not parssable, assume it is a name instead
+                logger.debug('looking for peer by name')
+                peer_address = await self.find_peer_by_name(peer_address, transport)
+
+        # Create a future so that we can wait for the connection's result
+        pending_connection = asyncio.get_running_loop().create_future()
+        self.on('connection', pending_connection.set_result)
+        self.on('connection_failure', pending_connection.set_exception)
+
+        # Tell the controller to connect
+        if transport == BT_LE_TRANSPORT:
+            # TODO: use real values, not fixed ones
+            result = await self.send_command(HCI_LE_Create_Connection_Command(
+                le_scan_interval        = 96,
+                le_scan_window          = 96,
+                initiator_filter_policy = 0,
+                peer_address_type       = peer_address.address_type,
+                peer_address            = peer_address,
+                own_address_type        = Address.RANDOM_DEVICE_ADDRESS,
+                conn_interval_min       = 12,
+                conn_interval_max       = 24,
+                conn_latency            = 0,
+                supervision_timeout     = 72,
+                minimum_ce_length       = 0,
+                maximum_ce_length       = 0
+            ))
+        else:
+            # TODO: use real values, not fixed ones
+            result = await self.send_command(HCI_Create_Connection_Command(
+                bd_addr                   = peer_address,
+                packet_type               = 0xCC18,  # FIXME: change
+                page_scan_repetition_mode = HCI_R2_PAGE_SCAN_REPETITION_MODE,
+                clock_offset              = 0x0000,
+                allow_role_switch         = 0x01,
+                reserved                  = 0
+            ))
+
+        try:
+            if result.status != HCI_Command_Status_Event.PENDING:
+                raise RuntimeError(f'HCI_LE_Create_Connection_Command failed: {HCI_Constant.status_name(result.status)} ({result.status})')
+
+            # Wait for the connection process to complete
+            self.connecting = True
+            return await pending_connection
+        finally:
+            self.remove_listener('connection', pending_connection.set_result)
+            self.remove_listener('connection_failure', pending_connection.set_exception)
+            self.connecting = False
+
+    @property
+    def is_connecting(self):
+        return self.connecting
+
+    @property
+    def is_disconnecting(self):
+        return self.disconnecting
+
+    async def cancel_connection(self):
+        if not self.is_connecting:
+            return
+        await self.send_command(HCI_LE_Create_Connection_Cancel_Command())
+
+    async def disconnect(self, connection, reason):
+        # Create a future so that we can wait for the disconnection's result
+        pending_disconnection = asyncio.get_running_loop().create_future()
+        connection.on('disconnection', pending_disconnection.set_result)
+        connection.on('disconnection_failure', pending_disconnection.set_exception)
+
+        # Request a disconnection
+        result = await self.send_command(HCI_Disconnect_Command(connection_handle = connection.handle, reason = reason))
+
+        try:
+            if result.status != HCI_Command_Status_Event.PENDING:
+                raise RuntimeError(f'HCI_Disconnect_Command failed: {HCI_Constant.status_name(result.status)} ({result.status})')
+
+            # Wait for the disconnection process to complete
+            self.disconnecting = True
+            return await pending_disconnection
+        finally:
+            connection.remove_listener('disconnection', pending_disconnection.set_result)
+            connection.remove_listener('disconnection_failure', pending_disconnection.set_exception)
+            self.disconnecting = False
+
+    async def update_connection_parameters(
+        self,
+        connection,
+        conn_interval_min,
+        conn_interval_max,
+        conn_latency,
+        supervision_timeout,
+        minimum_ce_length = 0,
+        maximum_ce_length = 0
+    ):
+        '''
+        NOTE: the name of the parameters may look odd, but it just follows the names used in the Bluetooth spec.
+        '''
+        await self.send_command(HCI_LE_Connection_Update_Command(
+            connection_handle   = connection.handle,
+            conn_interval_min   = conn_interval_min,
+            conn_interval_max   = conn_interval_max,
+            conn_latency        = conn_latency,
+            supervision_timeout = supervision_timeout,
+            minimum_ce_length   = minimum_ce_length,
+            maximum_ce_length   = maximum_ce_length
+        ))
+        # TODO: check result
+
+    async def find_peer_by_name(self, name, transport=BT_LE_TRANSPORT):
+        """
+        Scan for a peer with a give name and return its address and transport
+        """
+
+        # Create a future to wait for an address to be found
+        peer_address = asyncio.get_running_loop().create_future()
+
+        # Scan/inquire with event handlers to handle scan/inquiry results
+        def on_peer_found(address, ad_data):
+            local_name = ad_data.get(AdvertisingData.COMPLETE_LOCAL_NAME)
+            if local_name is None:
+                local_name = ad_data.get(AdvertisingData.SHORTENED_LOCAL_NAME)
+            if local_name is not None:
+                if local_name.decode('utf-8') == name:
+                    peer_address.set_result(address)
+        try:
+            handler = None
+            if transport == BT_LE_TRANSPORT:
+                event_name = 'advertisement'
+                handler = self.on(
+                    event_name,
+                    lambda address, ad_data, rssi, connectable:
+                        on_peer_found(address, ad_data)
+                )
+
+                was_scanning = self.scanning
+                if not self.scanning:
+                    await self.start_scanning(filter_duplicates=True)
+
+            elif transport == BT_BR_EDR_TRANSPORT:
+                event_name = 'inquiry_result'
+                handler = self.on(
+                    event_name,
+                    lambda address, class_of_device, eir_data, rssi:
+                        on_peer_found(address, eir_data)
+                )
+
+                was_discovering = self.discovering
+                if not self.discovering:
+                    await self.start_discovery()
+            else:
+                return None
+
+            return await peer_address
+        finally:
+            if handler is not None:
+                self.remove_listener(event_name, handler)
+
+            if transport == BT_LE_TRANSPORT and not was_scanning:
+                await self.stop_scanning()
+            elif transport == BT_BR_EDR_TRANSPORT and not was_discovering:
+                await self.stop_discovery()
+
+    @property
+    def pairing_config_factory(self):
+        return self.smp_manager.pairing_config_factory
+
+    @pairing_config_factory.setter
+    def pairing_config_factory(self, pairing_config_factory):
+        self.smp_manager.pairing_config_factory = pairing_config_factory
+
+    async def pair(self, connection):
+        return await self.smp_manager.pair(connection)
+
+    def request_pairing(self, connection):
+        return self.smp_manager.request_pairing(connection)
+
+    async def get_long_term_key(self, connection_handle, rand, ediv):
+        if (connection := self.lookup_connection(connection_handle)) is None:
+            return
+
+        # Start by looking for the key in an SMP session
+        ltk = self.smp_manager.get_long_term_key(connection, rand, ediv)
+        if ltk is not None:
+            return ltk
+
+        # Then look for the key in the keystore
+        if self.keystore is not None:
+            keys = await self.keystore.get(str(connection.peer_address))
+            if keys is not None:
+                logger.debug('found keys in the key store')
+                if keys.ltk:
+                    return keys.ltk.value
+                elif connection.role == BT_CENTRAL_ROLE and keys.ltk_central:
+                    return keys.ltk_central.value
+                elif connection.role == BT_PERIPHERAL_ROLE and keys.ltk_peripheral:
+                    return keys.ltk_peripheral.value
+
+    async def get_link_key(self, address):
+        # Look for the key in the keystore
+        if self.keystore is not None:
+            keys = await self.keystore.get(str(address))
+            if keys is not None:
+                logger.debug('found keys in the key store')
+                return keys.link_key.value
+
+    # [Classic only]
+    async def authenticate(self, connection):
+        # Set up event handlers
+        pending_authentication = asyncio.get_running_loop().create_future()
+
+        def on_authentication():
+            pending_authentication.set_result(None)
+
+        def on_authentication_failure(error_code):
+            pending_authentication.set_exception(HCI_Error(error_code))
+
+        connection.on('connection_authentication', on_authentication)
+        connection.on('connection_authentication_failure',  on_authentication_failure)
+
+        # Request the authentication
+        try:
+            result = await self.send_command(
+                HCI_Authentication_Requested_Command(connection_handle = connection.handle)
+            )
+            if result.status != HCI_COMMAND_STATUS_PENDING:
+                logger.warn(f'HCI_Authentication_Requested_Command failed: {HCI_Constant.error_name(result.status)}')
+                raise HCI_Error(result.status)
+
+            # Wait for the authentication to complete
+            await pending_authentication
+        finally:
+            connection.remove_listener('connection_authentication', on_authentication)
+            connection.remove_listener('connection_authentication_failure',  on_authentication_failure)
+
+    async def encrypt(self, connection):
+        # Set up event handlers
+        pending_encryption = asyncio.get_running_loop().create_future()
+
+        def on_encryption_change():
+            pending_encryption.set_result(None)
+
+        def on_encryption_failure(error_code):
+            pending_encryption.set_exception(HCI_Error(error_code))
+
+        connection.on('connection_encryption_change',  on_encryption_change)
+        connection.on('connection_encryption_failure', on_encryption_failure)
+
+        # Request the encryption
+        try:
+            if connection.transport == BT_LE_TRANSPORT:
+                # Look for a key in the key store
+                if self.keystore is None:
+                    raise RuntimeError('no key store')
+
+                keys = await self.keystore.get(str(connection.peer_address))
+                if keys is None:
+                    raise RuntimeError('keys not found in key store')
+
+                if keys.ltk is not None:
+                    ltk  = keys.ltk.value
+                    rand = bytes(8)
+                    ediv = 0
+                elif keys.ltk_central is not None:
+                    ltk  = keys.ltk_central.value
+                    rand = keys.ltk_central.rand
+                    ediv = keys.ltk_central.ediv
+                else:
+                    raise RuntimeError('no LTK found for peer')
+
+                if connection.role != HCI_CENTRAL_ROLE:
+                    raise InvalidStateError('only centrals can start encryption')
+
+                result = await self.send_command(
+                    HCI_LE_Start_Encryption_Command(
+                        connection_handle     = connection.handle,
+                        random_number         = rand,
+                        encrypted_diversifier = ediv,
+                        long_term_key         = ltk
+                    )
+                )
+
+                if result.status != HCI_COMMAND_STATUS_PENDING:
+                    logger.warn(f'HCI_LE_Start_Encryption_Command failed: {HCI_Constant.error_name(result.status)}')
+                    raise HCI_Error(result.status)
+            else:
+                result = await self.send_command(
+                    HCI_Set_Connection_Encryption_Command(
+                        connection_handle = connection.handle,
+                        encryption_enable = 0x01
+                    )
+                )
+
+                if result.status != HCI_COMMAND_STATUS_PENDING:
+                    logger.warn(f'HCI_Set_Connection_Encryption_Command failed: {HCI_Constant.error_name(result.status)}')
+                    raise HCI_Error(result.status)
+
+            # Wait for the result
+            await pending_encryption
+        finally:
+            connection.remove_listener('connection_encryption_change',  on_encryption_change)
+            connection.remove_listener('connection_encryption_failure', on_encryption_failure)
+
+    # [Classic only]
+    async def request_remote_name(self, connection):
+        # Set up event handlers
+        pending_name = asyncio.get_running_loop().create_future()
+
+        def on_remote_name():
+            pending_name.set_result(connection.peer_name)
+
+        def on_remote_name_failure(error_code):
+            pending_name.set_exception(HCI_Error(error_code))
+
+        connection.on('remote_name', on_remote_name)
+        connection.on('remote_name_failure', on_remote_name_failure)
+
+        try:
+            result = await self.send_command(
+                HCI_Remote_Name_Request_Command(
+                    bd_addr                   = connection.peer_address,
+                    page_scan_repetition_mode = HCI_Remote_Name_Request_Command.R0,  # TODO investigate other options
+                    reserved                  = 0,
+                    clock_offset              = 0  # TODO investigate non-0 values
+                )
+            )
+
+            if result.status != HCI_COMMAND_STATUS_PENDING:
+                logger.warn(f'HCI_Set_Connection_Encryption_Command failed: {HCI_Constant.error_name(result.status)}')
+                raise HCI_Error(result.status)
+
+            # Wait for the result
+            return await pending_name
+        finally:
+            connection.remove_listener('remote_name', on_remote_name)
+            connection.remove_listener('remote_name_failure', on_remote_name_failure)
+
+    # [Classic only]
+    @host_event_handler
+    def on_link_key(self, bd_addr, link_key, key_type):
+        # Store the keys in the key store
+        if self.keystore:
+            pairing_keys = keys.PairingKeys()
+            pairing_keys.link_key = keys.PairingKeys.Key(value = link_key)
+
+            async def store_keys():
+                try:
+                    await self.keystore.update(str(bd_addr), pairing_keys)
+                except Exception as error:
+                    logger.warn(f'!!! error while storing keys: {error}')
+
+            asyncio.create_task(store_keys())
+
+    def add_service(self, service):
+        self.gatt_server.add_service(service)
+
+    def add_services(self, services):
+        self.gatt_server.add_services(services)
+
+    async def notify_subscriber(self, connection, attribute, force=False):
+        await self.gatt_server.notify_subscriber(connection, attribute, force)
+
+    async def notify_subscribers(self, attribute, force=False):
+        await self.gatt_server.notify_subscribers(attribute, force)
+
+    async def indicate_subscriber(self, connection, attribute, force=False):
+        await self.gatt_server.indicate_subscriber(connection, attribute, force)
+
+    async def indicate_subscribers(self, attribute):
+        await self.gatt_server.indicate_subscribers(attribute)
+
+    @host_event_handler
+    def on_connection(self, connection_handle, transport, peer_address, peer_resolvable_address, role, connection_parameters):
+        logger.debug(f'*** Connection: [0x{connection_handle:04X}] {peer_address} as {HCI_Constant.role_name(role)}')
+        if connection_handle in self.connections:
+            logger.warn('new connection reuses the same handle as a previous connection')
+
+        # Resolve the peer address if we can
+        if self.address_resolver:
+            if peer_address.is_resolvable:
+                resolved_address = self.address_resolver.resolve(peer_address)
+                if resolved_address is not None:
+                    logger.debug(f'*** Address resolved as {resolved_address}')
+                    peer_resolvable_address = peer_address
+                    peer_address = resolved_address
+
+        # Create a new connection
+        connection = Connection(
+            self,
+            connection_handle,
+            transport,
+            peer_address,
+            peer_resolvable_address,
+            role,
+            connection_parameters
+        )
+        self.connections[connection_handle] = connection
+
+        # We are no longer advertising
+        self.advertising = False
+
+        # Emit an event to notify listeners of the new connection
+        self.emit('connection', connection)
+
+    @host_event_handler
+    def on_connection_failure(self, error_code):
+        logger.debug(f'*** Connection failed: {error_code}')
+        error = ConnectionError(
+            error_code,
+            'hci',
+            HCI_Constant.error_name(error_code)
+        )
+        self.emit('connection_failure', error)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_disconnection(self, connection, reason):
+        logger.debug(f'*** Disconnection: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, reason={reason}')
+        connection.emit('disconnection', reason)
+
+        # Remove the connection from the map
+        del self.connections[connection.handle]
+
+        # Cleanup subsystems that maintain per-connection state
+        self.gatt_server.on_disconnection(connection)
+
+        # Restart advertising if auto-restart is enabled
+        if self.auto_restart_advertising:
+            logger.debug('restarting advertising')
+            asyncio.create_task(self.start_advertising(auto_restart=self.auto_restart_advertising))
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_disconnection_failure(self, connection, error_code):
+        logger.debug(f'*** Disconnection failed: {error_code}')
+        error = ConnectionError(
+            error_code,
+            'hci',
+            HCI_Constant.error_name(error_code)
+        )
+        connection.emit('disconnection_failure', error)
+
+    @host_event_handler
+    @AsyncRunner.run_in_task()
+    async def on_inquiry_complete(self):
+        if self.discovering:
+            # Inquire again
+            await self.start_discovery()
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_authentication(self, connection):
+        logger.debug(f'*** Connection Authentication: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}')
+        connection.authenticated = True
+        connection.emit('connection_authentication')
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_authentication_failure(self, connection, error):
+        logger.debug(f'*** Connection Authentication Failure: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}')
+        connection.emit('connection_authentication_failure', error)
+
+    # [Classic only]
+    @host_event_handler
+    @with_connection_from_address
+    def on_authentication_io_capability_request(self, connection):
+        # Ask what the pairing config should be for this connection
+        pairing_config = self.pairing_config_factory(connection)
+
+        # Map the SMP IO capability to a Classic IO capability
+        io_capability = {
+            smp.SMP_DISPLAY_ONLY_IO_CAPABILITY:       HCI_DISPLAY_ONLY_IO_CAPABILITY,
+            smp.SMP_DISPLAY_YES_NO_IO_CAPABILITY:     HCI_DISPLAY_YES_NO_IO_CAPABILITY,
+            smp.SMP_KEYBOARD_ONLY_IO_CAPABILITY:      HCI_KEYBOARD_ONLY_IO_CAPABILITY,
+            smp.SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
+            smp.SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   HCI_DISPLAY_YES_NO_IO_CAPABILITY
+        }.get(pairing_config.delegate.io_capability)
+
+        if io_capability is None:
+            logger.warning(f'cannot map IO capability ({pairing_config.delegate.io_capability}')
+            io_capability = HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
+
+        # Compute the authentication requirements
+        authentication_requirements = (
+            # No Bonding
+            (
+                HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
+                HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS
+            ),
+            # General Bonding
+            (
+                HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
+                HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS
+            )
+        )[1 if pairing_config.bonding else 0][1 if pairing_config.mitm else 0]
+
+        # Respond
+        self.host.send_command_sync(
+            HCI_IO_Capability_Request_Reply_Command(
+                bd_addr                     = connection.peer_address,
+                io_capability               = io_capability,
+                oob_data_present            = 0x00,  # Not present
+                authentication_requirements = authentication_requirements
+            )
+        )
+
+    # [Classic only]
+    @host_event_handler
+    @with_connection_from_address
+    def on_authentication_user_confirmation_request(self, connection, code):
+        # Ask what the pairing config should be for this connection
+        pairing_config = self.pairing_config_factory(connection)
+
+        can_confirm = pairing_config.delegate.io_capability not in {
+            smp.SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
+            smp.SMP_DISPLAY_ONLY_IO_CAPABILITY
+        }
+
+        # Respond
+        if can_confirm and pairing_config.delegate:
+            async def compare_numbers():
+                numbers_match = await pairing_config.delegate.compare_numbers(code, digits=6)
+                if numbers_match:
+                    self.host.send_command_sync(
+                        HCI_User_Confirmation_Request_Reply_Command(bd_addr=connection.peer_address)
+                    )
+                else:
+                    self.host.send_command_sync(
+                        HCI_User_Confirmation_Request_Negative_Reply_Command(bd_addr=connection.peer_address)
+                    )
+
+            asyncio.create_task(compare_numbers())
+        else:
+            self.host.send_command_sync(
+                HCI_User_Confirmation_Request_Reply_Command(bd_addr=connection.peer_address)
+            )
+
+    # [Classic only]
+    @host_event_handler
+    @with_connection_from_address
+    def on_authentication_user_passkey_request(self, connection):
+        # Ask what the pairing config should be for this connection
+        pairing_config = self.pairing_config_factory(connection)
+
+        can_input = pairing_config.delegate.io_capability in {
+            smp.SMP_KEYBOARD_ONLY_IO_CAPABILITY,
+            smp.SMP_KEYBOARD_DISPLAY_IO_CAPABILITY
+        }
+
+        # Respond
+        if can_input and pairing_config.delegate:
+            async def get_number():
+                number = await pairing_config.delegate.get_number()
+                if number is not None:
+                    self.host.send_command_sync(
+                        HCI_User_Passkey_Request_Reply_Command(
+                            bd_addr       = connection.peer_address,
+                            numeric_value = number)
+                    )
+                else:
+                    self.host.send_command_sync(
+                        HCI_User_Passkey_Request_Negative_Reply_Command(bd_addr=connection.peer_address)
+                    )
+
+            asyncio.create_task(get_number())
+        else:
+            self.host.send_command_sync(
+                HCI_User_Passkey_Request_Negative_Reply_Command(bd_addr=connection.peer_address)
+            )
+
+    # [Classic only]
+    @host_event_handler
+    @with_connection_from_address
+    def on_remote_name(self, connection, remote_name):
+        # Try to decode the name
+        try:
+            connection.peer_name = remote_name.decode('utf-8')
+            connection.emit('remote_name')
+        except UnicodeDecodeError as error:
+            logger.warning('peer name is not valid UTF-8')
+            connection.emit('remote_name_failure', error)
+
+    # [Classic only]
+    @host_event_handler
+    @with_connection_from_address
+    def on_remote_name_failure(self, connection, error):
+        connection.emit('remote_name_failure', error)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_encryption_change(self, connection, encryption):
+        logger.debug(f'*** Connection Encryption Change: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, encryption={encryption}')
+        connection.encryption = encryption
+        connection.emit('connection_encryption_change')
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_encryption_failure(self, connection, error):
+        logger.debug(f'*** Connection Encryption Failure: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}')
+        connection.emit('connection_encryption_failure', error)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_encryption_key_refresh(self, connection):
+        logger.debug(f'*** Connection Key Refresh: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}')
+        connection.emit('connection_encryption_key_refresh')
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_parameters_update(self, connection, connection_parameters):
+        logger.debug(f'*** Connection Parameters Update: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, {connection_parameters}')
+        connection.parameters = connection_parameters
+        connection.emit('connection_parameters_update')
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_parameters_update_failure(self, connection, error):
+        logger.debug(f'*** Connection Parameters Update Failed: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}')
+        connection.emit('connection_parameters_update_failure', error)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_phy_update(self, connection, connection_phy):
+        logger.debug(f'*** Connection PHY Update: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, {connection_phy}')
+        connection.phy = connection_phy
+        connection.emit('connection_phy_update')
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_phy_update_failure(self, connection, error):
+        logger.debug(f'*** Connection PHY Update Failed: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}')
+        connection.emit('connection_phy_update_failure', error)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_att_mtu_update(self, connection, att_mtu):
+        logger.debug(f'*** Connection ATT MTU Update: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, {att_mtu}')
+        connection.att_mtu = att_mtu
+        connection.emit('connection_att_mtu_update')
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_connection_data_length_change(self, connection, max_tx_octets, max_tx_time, max_rx_octets, max_rx_time):
+        logger.debug(f'*** Connection Data Length Change: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}')
+        connection.data_length = (max_tx_octets, max_tx_time, max_rx_octets, max_rx_time)
+        connection.emit('connection_data_length_change')
+
+    @with_connection_from_handle
+    def on_pairing_start(self, connection):
+        connection.emit('pairing_start')
+
+    @with_connection_from_handle
+    def on_pairing(self, connection, keys):
+        connection.emit('pairing', keys)
+
+    @with_connection_from_handle
+    def on_pairing_failure(self, connection, reason):
+        connection.emit('pairing_failure', reason)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_gatt_pdu(self, connection, pdu):
+        # Parse the L2CAP payload into an ATT PDU object
+        att_pdu = ATT_PDU.from_bytes(pdu)
+
+        # Conveniently, even-numbered op codes are client->server and
+        # odd-numbered ones are server->client
+        if att_pdu.op_code & 1:
+            if connection.gatt_client is None:
+                logger.warn(color('no GATT client for connection 0x{connection_handle:04X}'))
+                return
+            connection.gatt_client.on_gatt_pdu(att_pdu)
+        else:
+            if connection.gatt_server is None:
+                logger.warn(color('no GATT server for connection 0x{connection_handle:04X}'))
+                return
+            connection.gatt_server.on_gatt_pdu(connection, att_pdu)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_smp_pdu(self, connection, pdu):
+        self.smp_manager.on_smp_pdu(connection, pdu)
+
+    @host_event_handler
+    @with_connection_from_handle
+    def on_l2cap_pdu(self, connection, cid, pdu):
+        self.l2cap_channel_manager.on_pdu(connection, cid, pdu)
+
+    def __str__(self):
+        return f'Device(name="{self.name}", random_address="{self.random_address}"", public_address="{self.public_address}")'
diff --git a/bumble/gap.py b/bumble/gap.py
new file mode 100644
index 0000000..8341215
--- /dev/null
+++ b/bumble/gap.py
@@ -0,0 +1,59 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import struct
+
+from .gatt import (
+    Service,
+    Characteristic,
+    GATT_GENERIC_ACCESS_SERVICE,
+    GATT_DEVICE_NAME_CHARACTERISTIC,
+    GATT_APPEARANCE_CHARACTERISTIC
+)
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Classes
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+class GenericAccessService(Service):
+    def __init__(self, device_name, appearance = (0, 0)):
+        device_name_characteristic = Characteristic(
+            GATT_DEVICE_NAME_CHARACTERISTIC,
+            Characteristic.READ,
+            Characteristic.READABLE,
+            device_name.encode('utf-8')[:248]
+        )
+
+        appearance_characteristic = Characteristic(
+            GATT_APPEARANCE_CHARACTERISTIC,
+            Characteristic.READ,
+            Characteristic.READABLE,
+            struct.pack('<H', (appearance[0] << 6) | appearance[1])
+        )
+
+        super().__init__(GATT_GENERIC_ACCESS_SERVICE, [
+            device_name_characteristic,
+            appearance_characteristic
+        ])
diff --git a/bumble/gatt.py b/bumble/gatt.py
new file mode 100644
index 0000000..df760c3
--- /dev/null
+++ b/bumble/gatt.py
@@ -0,0 +1,424 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# GATT - Generic Attribute Profile
+#
+# See Bluetooth spec @ Vol 3, Part G
+#
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import types
+import logging
+from colors import color
+
+from .core import *
+from .hci import *
+from .att import *
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+GATT_REQUEST_TIMEOUT = 30  # seconds
+
+GATT_MAX_ATTRIBUTE_VALUE_SIZE = 512
+
+# Services
+GATT_GENERIC_ACCESS_SERVICE                 = UUID.from_16_bits(0x1800, 'Generic Access')
+GATT_GENERIC_ATTRIBUTE_SERVICE              = UUID.from_16_bits(0x1801, 'Generic Attribute')
+GATT_IMMEDIATE_ALERT_SERVICE                = UUID.from_16_bits(0x1802, 'Immediate Alert')
+GATT_LINK_LOSS_SERVICE                      = UUID.from_16_bits(0x1803, 'Link Loss')
+GATT_TX_POWER_SERVICE                       = UUID.from_16_bits(0x1804, 'TX Power')
+GATT_CURRENT_TIME_SERVICE                   = UUID.from_16_bits(0x1805, 'Current Time')
+GATT_REFERENCE_TIME_UPDATE_SERVICE          = UUID.from_16_bits(0x1806, 'Reference Time Update')
+GATT_NEXT_DST_CHANGE_SERVICE                = UUID.from_16_bits(0x1807, 'Next DST Change')
+GATT_GLUCOSE_SERVICE                        = UUID.from_16_bits(0x1808, 'Glucose')
+GATT_HEALTH_THERMOMETER_SERVICE             = UUID.from_16_bits(0x1809, 'Health Thermometer')
+GATT_DEVICE_INFORMATION_SERVICE             = UUID.from_16_bits(0x180A, 'Device Information')
+GATT_HEART_RATE_SERVICE                     = UUID.from_16_bits(0x180D, 'Heart Rate')
+GATT_PHONE_ALERT_STATUS_SERVICE             = UUID.from_16_bits(0x180E, 'Phone Alert Status')
+GATT_BATTERY_SERVICE                        = UUID.from_16_bits(0x180F, 'Battery')
+GATT_BLOOD_PRESSURE_SERVICE                 = UUID.from_16_bits(0x1810, 'Blood Pressure')
+GATT_ALERT_NOTIFICATION_SERVICE             = UUID.from_16_bits(0x1811, 'Alert Notification')
+GATT_HUMAN_INTERFACE_DEVICE_SERVICE         = UUID.from_16_bits(0x1812, 'Human Interface Device')
+GATT_SCAN_PARAMETERS_SERVICE                = UUID.from_16_bits(0x1813, 'Scan Parameters')
+GATT_RUNNING_SPEED_AND_CADENCE_SERVICE      = UUID.from_16_bits(0x1814, 'Running Speed and Cadence')
+GATT_AUTOMATION_IO_SERVICE                  = UUID.from_16_bits(0x1815, 'Automation IO')
+GATT_CYCLING_SPEED_AND_CADENCE_SERVICE      = UUID.from_16_bits(0x1816, 'Cycling Speed and Cadence')
+GATT_CYCLING_POWER_SERVICE                  = UUID.from_16_bits(0x1818, 'Cycling Power')
+GATT_LOCATION_AND_NAVIGATION_SERVICE        = UUID.from_16_bits(0x1819, 'Location and Navigation')
+GATT_ENVIRONMENTAL_SENSING_SERVICE          = UUID.from_16_bits(0x181A, 'Environmental Sensing')
+GATT_BODY_COMPOSITION_SERVICE               = UUID.from_16_bits(0x181B, 'Body Composition')
+GATT_USER_DATA_SERVICE                      = UUID.from_16_bits(0x181C, 'User Data')
+GATT_WEIGHT_SCALE_SERVICE                   = UUID.from_16_bits(0x181D, 'Weight Scale')
+GATT_BOND_MANAGEMENT_SERVICE                = UUID.from_16_bits(0x181E, 'Bond Management')
+GATT_CONTINUOUS_GLUCOSE_MONITORING_SERVICE  = UUID.from_16_bits(0x181F, 'Continuous Glucose Monitoring')
+GATT_INTERNET_PROTOCOL_SUPPORT_SERVICE      = UUID.from_16_bits(0x1820, 'Internet Protocol Support')
+GATT_INDOOR_POSITIONING_SERVICE             = UUID.from_16_bits(0x1821, 'Indoor Positioning')
+GATT_PULSE_OXIMETER_SERVICE                 = UUID.from_16_bits(0x1822, 'Pulse Oximeter')
+GATT_HTTP_PROXY_SERVICE                     = UUID.from_16_bits(0x1823, 'HTTP Proxy')
+GATT_TRANSPORT_DISCOVERY_SERVICE            = UUID.from_16_bits(0x1824, 'Transport Discovery')
+GATT_OBJECT_TRANSFER_SERVICE                = UUID.from_16_bits(0x1825, 'Object Transfer')
+GATT_FITNESS_MACHINE_SERVICE                = UUID.from_16_bits(0x1826, 'Fitness Machine')
+GATT_MESH_PROVISIONING_SERVICE              = UUID.from_16_bits(0x1827, 'Mesh Provisioning')
+GATT_MESH_PROXY_SERVICE                     = UUID.from_16_bits(0x1828, 'Mesh Proxy')
+GATT_RECONNECTION_CONFIGURATION_SERVICE     = UUID.from_16_bits(0x1829, 'Reconnection Configuration')
+GATT_INSULIN_DELIVERY_SERVICE               = UUID.from_16_bits(0x183A, 'Insulin Delivery')
+GATT_BINARY_SENSOR_SERVICE                  = UUID.from_16_bits(0x183B, 'Binary Sensor')
+GATT_EMERGENCY_CONFIGURATION_SERVICE        = UUID.from_16_bits(0x183C, 'Emergency Configuration')
+GATT_PHYSICAL_ACTIVITY_MONITOR_SERVICE      = UUID.from_16_bits(0x183E, 'Physical Activity Monitor')
+GATT_AUDIO_INPUT_CONTROL_SERVICE            = UUID.from_16_bits(0x1843, 'Audio Input Control')
+GATT_VOLUME_CONTROL_SERVICE                 = UUID.from_16_bits(0x1844, 'Volume Control')
+GATT_VOLUME_OFFSET_CONTROL_SERVICE          = UUID.from_16_bits(0x1845, 'Volume Offset Control')
+GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification Service')
+GATT_DEVICE_TIME_SERVICE                    = UUID.from_16_bits(0x1847, 'Device Time')
+GATT_MEDIA_CONTROL_SERVICE                  = UUID.from_16_bits(0x1848, 'Media Control Service')
+GATT_GENERIC_MEDIA_CONTROL_SERVICE          = UUID.from_16_bits(0x1849, 'Generic Media Control Service')
+GATT_CONSTANT_TONE_EXTENSION_SERVICE        = UUID.from_16_bits(0x184A, 'Constant Tone Extension')
+GATT_TELEPHONE_BEARER_SERVICE               = UUID.from_16_bits(0x184B, 'Telephone Bearer Service')
+GATT_GENERIC_TELEPHONE_BEARER_SERVICE       = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer Service')
+GATT_MICROPHONE_CONTROL_SERVICE             = UUID.from_16_bits(0x184D, 'Microphone Control')
+
+# Types
+GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE   = UUID.from_16_bits(0x2800, 'Primary Service')
+GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2801, 'Secondary Service')
+GATT_INCLUDE_ATTRIBUTE_TYPE           = UUID.from_16_bits(0x2802, 'Include')
+GATT_CHARACTERISTIC_ATTRIBUTE_TYPE    = UUID.from_16_bits(0x2803, 'Characteristic')
+
+# Descriptors
+GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR   = UUID.from_16_bits(0x2900, 'Characteristic Extended Properties')
+GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR      = UUID.from_16_bits(0x2901, 'Characteristic User Description')
+GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x2902, 'Client Characteristic Configuration')
+GATT_SERVER_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x2903, 'Server Characteristic Configuration')
+GATT_CHARACTERISTIC_PRESENTATION_FORMAT_DESCRIPTOR   = UUID.from_16_bits(0x2904, 'Characteristic Format')
+GATT_CHARACTERISTIC_AGGREGATE_FORMAT_DESCRIPTOR      = UUID.from_16_bits(0x2905, 'Characteristic Aggregate Format')
+GATT_VALID_RANGE_DESCRIPTOR                          = UUID.from_16_bits(0x2906, 'Valid Range')
+GATT_EXTERNAL_REPORT_DESCRIPTOR                      = UUID.from_16_bits(0x2907, 'External Report')
+GATT_REPORT_REFERENCE_DESCRIPTOR                     = UUID.from_16_bits(0x2908, 'Report Reference')
+GATT_NUMBER_OF_DIGITALS_DESCRIPTOR                   = UUID.from_16_bits(0x2909, 'Number of Digitals')
+GATT_VALUE_TRIGGER_SETTING_DESCRIPTOR                = UUID.from_16_bits(0x290A, 'Value Trigger Setting')
+GATT_ENVIRONMENTAL_SENSING_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x290B, 'Environmental Sensing Configuration')
+GATT_ENVIRONMENTAL_SENSING_MEASUREMENT_DESCRIPTOR    = UUID.from_16_bits(0x290C, 'Environmental Sensing Measurement')
+GATT_ENVIRONMENTAL_SENSING_TRIGGER_DESCRIPTOR        = UUID.from_16_bits(0x290D, 'Environmental Sensing Trigger Setting')
+GATT_TIME_TRIGGER_DESCRIPTOR                         = UUID.from_16_bits(0x290E, 'Time Trigger Setting')
+GATT_COMPLETE_BR_EDR_TRANSPORT_BLOCK_DATA_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Complete BR-EDR Transport Block Data')
+
+# Device Information Service
+GATT_SYSTEM_ID_CHARACTERISTIC                          = UUID.from_16_bits(0x2A23, 'System ID')
+GATT_MODEL_NUMBER_STRING_CHARACTERISTIC                = UUID.from_16_bits(0x2A24, 'Model Number String')
+GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC               = UUID.from_16_bits(0x2A25, 'Serial Number String')
+GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A26, 'Firmware Revision String')
+GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A27, 'Hardware Revision String')
+GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A28, 'Software Revision String')
+GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A29, 'Manufacturer Name String')
+GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2A2A, 'IEEE 11073-20601 Regulatory Certification Data List')
+GATT_PNP_ID_CHARACTERISTIC                             = UUID.from_16_bits(0x2A50, 'PnP ID')
+
+# Human Interface Device
+GATT_HID_INFORMATION_CHARACTERISTIC   = UUID.from_16_bits(0x2A4A, 'HID Information')
+GATT_REPORT_MAP_CHARACTERISTIC        = UUID.from_16_bits(0x2A4B, 'Report Map')
+GATT_HID_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A4C, 'HID Control Point')
+GATT_REPORT_CHARACTERISTIC            = UUID.from_16_bits(0x2A4D, 'Report')
+GATT_PROTOCOL_MODE_CHARACTERISTIC     = UUID.from_16_bits(0x2A4E, 'Protocol Mode')
+
+# Misc
+GATT_DEVICE_NAME_CHARACTERISTIC                                = UUID.from_16_bits(0x2A00, 'Device Name')
+GATT_APPEARANCE_CHARACTERISTIC                                 = UUID.from_16_bits(0x2A01, 'Appearance')
+GATT_PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC                    = UUID.from_16_bits(0x2A02, 'Peripheral Privacy Flag')
+GATT_RECONNECTION_ADDRESS_CHARACTERISTIC                       = UUID.from_16_bits(0x2A03, 'Reconnection Address')
+GATT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.from_16_bits(0x2A04, 'Peripheral Preferred Connection Parameters')
+GATT_SERVICE_CHANGED_CHARACTERISTIC                            = UUID.from_16_bits(0x2A05, 'Service Changed')
+GATT_ALERT_LEVEL_CHARACTERISTIC                                = UUID.from_16_bits(0x2A06, 'Alert Level')
+GATT_TX_POWER_LEVEL_CHARACTERISTIC                             = UUID.from_16_bits(0x2A07, 'Tx Power Level')
+GATT_BATTERY_LEVEL_CHARACTERISTIC                              = UUID.from_16_bits(0x2A19, 'Battery Level')
+GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC                 = UUID.from_16_bits(0x2A22, 'Boot Keyboard Input Report')
+GATT_CURRENT_TIME_CHARACTERISTIC                               = UUID.from_16_bits(0x2A2B, 'Current Time')
+GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC                = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report')
+GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC                = UUID.from_16_bits(0x2AA6, 'Central Address Resolution')
+
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+
+def show_services(services):
+    for service in services:
+        print(color(str(service), 'cyan'))
+
+        for characteristic in service.characteristics:
+            print(color('  ' + str(characteristic), 'magenta'))
+
+            for descriptor in characteristic.descriptors:
+                print(color('    ' + str(descriptor), 'green'))
+
+
+# -----------------------------------------------------------------------------
+class Service(Attribute):
+    '''
+    See Vol 3, Part G - 3.1 SERVICE DEFINITION
+    '''
+
+    def __init__(self, uuid, characteristics, primary=True):
+        # Convert the uuid to a UUID object if it isn't already
+        if type(uuid) is str:
+            uuid = UUID(uuid)
+
+        super().__init__(
+            GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
+            Attribute.READABLE,
+            uuid.to_pdu_bytes()
+        )
+        self.uuid              = uuid
+        self.included_services = []
+        self.characteristics   = characteristics[:]
+        self.primary           = primary
+
+    def __str__(self):
+        return f'Service(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}){"" if self.primary else "*"}'
+
+
+# -----------------------------------------------------------------------------
+class TemplateService(Service):
+    '''
+    Convenience abstract class that can be used by profile-specific subclasses that want
+    to expose their UUID as a class property
+    '''
+    UUID = None
+
+    def __init__(self, characteristics, primary=True):
+        super().__init__(self.UUID, characteristics, primary)
+
+
+# -----------------------------------------------------------------------------
+class Characteristic(Attribute):
+    '''
+    See Vol 3, Part G - 3.3 CHARACTERISTIC DEFINITION
+    '''
+
+    # Property flags
+    BROADCAST                   = 0x01
+    READ                        = 0x02
+    WRITE_WITHOUT_RESPONSE      = 0x04
+    WRITE                       = 0x08
+    NOTIFY                      = 0x10
+    INDICATE                    = 0X20
+    AUTHENTICATED_SIGNED_WRITES = 0X40
+    EXTENDED_PROPERTIES         = 0X80
+
+    PROPERTY_NAMES = {
+        BROADCAST:                   'BROADCAST',
+        READ:                        'READ',
+        WRITE_WITHOUT_RESPONSE:      'WRITE_WITHOUT_RESPONSE',
+        WRITE:                       'WRITE',
+        NOTIFY:                      'NOTIFY',
+        INDICATE:                    'INDICATE',
+        AUTHENTICATED_SIGNED_WRITES: 'AUTHENTICATED_SIGNED_WRITES',
+        EXTENDED_PROPERTIES:         'EXTENDED_PROPERTIES'
+    }
+
+    @staticmethod
+    def property_name(property):
+        return Characteristic.PROPERTY_NAMES.get(property, '')
+
+    @staticmethod
+    def properties_as_string(properties):
+        return ','.join([
+            Characteristic.property_name(p) for p in Characteristic.PROPERTY_NAMES.keys()
+            if properties & p
+        ])
+
+    def __init__(self, uuid, properties, permissions, value = b'', descriptors = []):
+        super().__init__(uuid, permissions, value)
+        self.uuid        = self.type
+        self.properties  = properties
+        self.descriptors = descriptors
+
+    def get_descriptor(self, descriptor_type):
+        for descriptor in self.descriptors:
+            if descriptor.uuid == descriptor_type:
+                return descriptor
+
+    def __str__(self):
+        return f'Characteristic(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})'
+
+
+# -----------------------------------------------------------------------------
+class CharacteristicValue:
+    '''
+    Characteristic value where reading and/or writing is delegated to functions
+    passed as arguments to the constructor.
+    '''
+    def __init__(self, read=None, write=None):
+        self._read = read
+        self._write = write
+
+    def read(self, connection):
+        return self._read(connection) if self._read else b''
+
+    def write(self, connection, value):
+        if self._write:
+            self._write(connection, value)
+
+
+# -----------------------------------------------------------------------------
+class CharacteristicAdapter:
+    '''
+    An adapter that can adapt any object with `read_value` and `write_value`
+    methods (like Characteristic and CharacteristicProxy objects) by wrapping
+    those methods with ones that return/accept encoded/decoded values.
+    Objects with async methods are considered proxies, so the adaptation is one
+    where the return value of `read_value` is decoded and the value passed to
+    `write_value` is encoded. Other objects are considered local characteristics
+    so the adaptation is one where the return value of `read_value` is encoded
+    and the value passed to `write_value` is decoded.
+    If the characteristic has a `subscribe` method, it is wrapped with one where
+    the values are decoded before being passed to the subscriber.
+    '''
+    def __init__(self, characteristic):
+        self.wrapped_characteristic = characteristic
+
+        if (
+            asyncio.iscoroutinefunction(characteristic.read_value) and
+            asyncio.iscoroutinefunction(characteristic.write_value)
+        ):
+            self.read_value  = self.read_decoded_value
+            self.write_value = self.write_decoded_value
+        else:
+            self.read_value  = self.read_encoded_value
+            self.write_value = self.write_encoded_value
+
+        if hasattr(self.wrapped_characteristic, 'subscribe'):
+            self.subscribe = self.wrapped_subscribe
+
+    def __getattr__(self, name):
+        return getattr(self.wrapped_characteristic, name)
+
+    def read_encoded_value(self, connection):
+        return self.encode_value(self.wrapped_characteristic.read_value(connection))
+
+    def write_encoded_value(self, connection, value):
+        return self.wrapped_characteristic.write_value(connection, self.decode_value(value))
+
+    async def read_decoded_value(self):
+        return self.decode_value(await self.wrapped_characteristic.read_value())
+
+    async def write_decoded_value(self, value):
+        return await self.wrapped_characteristic.write_value(self.encode_value(value))
+
+    def encode_value(self, value):
+        return value
+
+    def decode_value(self, value):
+        return value
+
+    def wrapped_subscribe(self, subscriber=None):
+        return self.wrapped_characteristic.subscribe(
+            None if subscriber is None else lambda value: subscriber(self.decode_value(value))
+        )
+
+
+# -----------------------------------------------------------------------------
+class DelegatedCharacteristicAdapter(CharacteristicAdapter):
+    def __init__(self, characteristic, encode, decode):
+        super().__init__(characteristic)
+        self.encode = encode
+        self.decode = decode
+
+    def encode_value(self, value):
+        return self.encode(value)
+
+    def decode_value(self, value):
+        return self.decode(value)
+
+
+# -----------------------------------------------------------------------------
+class PackedCharacteristicAdapter(CharacteristicAdapter):
+    '''
+    Adapter that packs/unpacks characteristic values according to a standard
+    Python `struct` format.
+    For formats with a single value, the adapted `read_value` and `write_value`
+    methods return/accept single values. For formats with multiple values,
+    they return/accept a tuple with the same number of elements as is required for
+    the format.
+    '''
+    def __init__(self, characteristic, format):
+        super().__init__(characteristic)
+        self.struct = struct.Struct(format)
+
+    def pack(self, *values):
+        return self.struct.pack(*values)
+
+    def unpack(self, buffer):
+        return self.struct.unpack(buffer)
+
+    def encode_value(self, value):
+        return self.pack(*value if type(value) is tuple else (value,))
+
+    def decode_value(self, value):
+        unpacked = self.unpack(value)
+        return unpacked[0] if len(unpacked) == 1 else unpacked
+
+
+# -----------------------------------------------------------------------------
+class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
+    '''
+    Adapter that packs/unpacks characteristic values according to a standard
+    Python `struct` format.
+    The adapted `read_value` and `write_value` methods return/accept aa dictionary which
+    is packed/unpacked according to format, with the arguments extracted from the dictionary
+    by key, in the same order as they occur in the `keys` parameter.
+    '''
+    def __init__(self, characteristic, format, keys):
+        super().__init__(characteristic, format)
+        self.keys = keys
+
+    def pack(self, values):
+        return super().pack(*(values[key] for key in self.keys))
+
+    def unpack(self, buffer):
+        return dict(zip(self.keys, super().unpack(buffer)))
+
+
+# -----------------------------------------------------------------------------
+class UTF8CharacteristicAdapter(CharacteristicAdapter):
+    '''
+    Adapter that converts strings to/from bytes using UTF-8 encoding
+    '''
+    def encode_value(self, value):
+        return value.encode('utf-8')
+
+    def decode_value(self, value):
+        return value.decode('utf-8')
+
+
+# -----------------------------------------------------------------------------
+class Descriptor(Attribute):
+    '''
+    See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations
+    '''
+
+    def __init__(self, descriptor_type, permissions, value = b''):
+        super().__init__(descriptor_type, permissions, value)
+
+    def __str__(self):
+        return f'Descriptor(handle=0x{self.handle:04X}, type={self.type}, value={self.read_value(None).hex()})'
diff --git a/bumble/gatt_client.py b/bumble/gatt_client.py
new file mode 100644
index 0000000..e817e2e
--- /dev/null
+++ b/bumble/gatt_client.py
@@ -0,0 +1,728 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# GATT - Generic Attribute Profile
+# Client
+#
+# See Bluetooth spec @ Vol 3, Part G
+#
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import struct
+from colors import color
+
+from .core import ProtocolError, TimeoutError
+from .hci import *
+from .att import *
+from .gatt import (
+    GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
+    GATT_REQUEST_TIMEOUT,
+    GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
+    GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
+    GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
+    Characteristic
+)
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Proxies
+# -----------------------------------------------------------------------------
+class AttributeProxy(EventEmitter):
+    def __init__(self, client, handle, end_group_handle, attribute_type):
+        EventEmitter.__init__(self)
+        self.client           = client
+        self.handle           = handle
+        self.end_group_handle = end_group_handle
+        self.type             = attribute_type
+
+    async def read_value(self, no_long_read=False):
+        return await self.client.read_value(self.handle, no_long_read)
+
+    async def write_value(self, value, with_response=False):
+        return await self.client.write_value(self.handle, value, with_response)
+
+    def __str__(self):
+        return f'Attribute(handle=0x{self.handle:04X}, type={self.uuid})'
+
+
+class ServiceProxy(AttributeProxy):
+    @staticmethod
+    def from_client(cls, client, service_uuid):
+        # The service and its characteristics are considered to have already been discovered
+        services = client.get_services_by_uuid(service_uuid)
+        service = services[0] if services else None
+        return cls(service) if service else None
+
+    def __init__(self, client, handle, end_group_handle, uuid, primary=True):
+        attribute_type = GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE
+        super().__init__(client, handle, end_group_handle, attribute_type)
+        self.uuid            = uuid
+        self.characteristics = []
+
+    async def discover_characteristics(self, uuids=[]):
+        return await self.client.discover_characteristics(uuids, self)
+
+    def get_characteristics_by_uuid(self, uuid):
+        return self.client.get_characteristics_by_uuid(uuid, self)
+
+    def __str__(self):
+        return f'Service(handle=0x{self.handle:04X}, uuid={self.uuid})'
+
+
+class CharacteristicProxy(AttributeProxy):
+    def __init__(self, client, handle, end_group_handle, uuid, properties):
+        super().__init__(client, handle, end_group_handle, uuid)
+        self.uuid                   = uuid
+        self.properties             = properties
+        self.descriptors            = []
+        self.descriptors_discovered = False
+
+    def get_descriptor(self, descriptor_type):
+        for descriptor in self.descriptors:
+            if descriptor.type == descriptor_type:
+                return descriptor
+
+    async def discover_descriptors(self):
+        return await self.client.discover_descriptors(self)
+
+    async def subscribe(self, subscriber=None):
+        return await self.client.subscribe(self, subscriber)
+
+    def __str__(self):
+        return f'Characteristic(handle=0x{self.handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})'
+
+
+class DescriptorProxy(AttributeProxy):
+    def __init__(self, client, handle, descriptor_type):
+        super().__init__(client, handle, 0, descriptor_type)
+
+    def __str__(self):
+        return f'Descriptor(handle=0x{self.handle:04X}, type={self.type})'
+
+
+class ProfileServiceProxy:
+    '''
+    Base class for profile-specific service proxies
+    '''
+    @classmethod
+    def from_client(cls, client):
+        return ServiceProxy.from_client(cls, client, cls.SERVICE_CLASS.UUID)
+
+
+# -----------------------------------------------------------------------------
+# GATT Client
+# -----------------------------------------------------------------------------
+class Client:
+    def __init__(self, connection):
+        self.connection               = connection
+        self.mtu                      = ATT_DEFAULT_MTU
+        self.mtu_exchange_done        = False
+        self.request_semaphore        = asyncio.Semaphore(1)
+        self.pending_request          = None
+        self.pending_response         = None
+        self.notification_subscribers = {}  # Notification subscribers, by attribute handle
+        self.indication_subscribers   = {}  # Indication subscribers, by attribute handle
+        self.services                 = []
+
+    def send_gatt_pdu(self, pdu):
+        self.connection.send_l2cap_pdu(ATT_CID, pdu)
+
+    async def send_command(self, command):
+        logger.debug(f'GATT Command from client: [0x{self.connection.handle:04X}] {command}')
+        self.send_gatt_pdu(command.to_bytes())
+
+    async def send_request(self, request):
+        logger.debug(f'GATT Request from client: [0x{self.connection.handle:04X}] {request}')
+
+        # Wait until we can send (only one pending command at a time for the connection)
+        response = None
+        async with self.request_semaphore:
+            assert(self.pending_request is None)
+            assert(self.pending_response is None)
+
+            # Create a future value to hold the eventual response
+            self.pending_response = asyncio.get_running_loop().create_future()
+            self.pending_request  = request
+
+            try:
+                self.send_gatt_pdu(request.to_bytes())
+                response = await asyncio.wait_for(self.pending_response, GATT_REQUEST_TIMEOUT)
+            except asyncio.TimeoutError:
+                logger.warning(color('!!! GATT Request timeout', 'red'))
+                raise TimeoutError(f'GATT timeout for {request.name}')
+            finally:
+                self.pending_request  = None
+                self.pending_response = None
+
+        return response
+
+    def send_confirmation(self, confirmation):
+        logger.debug(f'GATT Confirmation from client: [0x{self.connection.handle:04X}] {confirmation}')
+        self.send_gatt_pdu(confirmation.to_bytes())
+
+    async def request_mtu(self, mtu):
+        # Check the range
+        if mtu < ATT_DEFAULT_MTU:
+            raise ValueError(f'MTU must be >= {ATT_DEFAULT_MTU}')
+        if mtu > 0xFFFF:
+            raise ValueError('MTU must be <= 0xFFFF')
+
+        # We can only send one request per connection
+        if self.mtu_exchange_done:
+            return
+
+        # Send the request
+        self.mtu_exchange_done = True
+        response = await self.send_request(ATT_Exchange_MTU_Request(client_rx_mtu = mtu))
+        if response.op_code == ATT_ERROR_RESPONSE:
+            raise ProtocolError(
+                response.error_code,
+                'att',
+                ATT_PDU.error_name(response.error_code),
+                response
+            )
+
+        self.mtu = max(ATT_DEFAULT_MTU, response.server_rx_mtu)
+        return self.mtu
+
+    def get_services_by_uuid(self, uuid):
+        return [service for service in self.services if service.uuid == uuid]
+
+    def get_characteristics_by_uuid(self, uuid, service = None):
+        services = [service] if service else self.services
+        return [c for c in [c for s in services for c in s.characteristics] if c.uuid == uuid]
+
+    def on_service_discovered(self, service):
+        ''' Add a service to the service list if it wasn't already there '''
+        already_known = False
+        for existing_service in self.services:
+            if existing_service.handle == service.handle:
+                already_known = True
+                break
+        if not already_known:
+            self.services.append(service)
+
+    async def discover_services(self, uuids = None):
+        '''
+        See Vol 3, Part G - 4.4.1 Discover All Primary Services
+        '''
+        starting_handle = 0x0001
+        services = []
+        while starting_handle < 0xFFFF:
+            response = await self.send_request(
+                ATT_Read_By_Group_Type_Request(
+                    starting_handle      = starting_handle,
+                    ending_handle        = 0xFFFF,
+                    attribute_group_type = GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE
+                )
+            )
+            if response is None:
+                # TODO raise appropriate exception
+                return []
+
+            # Check if we reached the end of the iteration
+            if response.op_code == ATT_ERROR_RESPONSE:
+                if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
+                    # Unexpected end
+                    logger.waning(f'!!! unexpected error while discovering services: {HCI_Constant.error_name(response.error_code)}')
+                    # TODO raise appropriate exception
+                    return
+                break
+
+            for attribute_handle, end_group_handle, attribute_value in response.attributes:
+                if attribute_handle < starting_handle or end_group_handle < attribute_handle:
+                    # Something's not right
+                    logger.warning(f'bogus handle values: {attribute_handle} {end_group_handle}')
+                    return
+
+                # Create a service proxy for this service
+                service = ServiceProxy(
+                    self,
+                    attribute_handle,
+                    end_group_handle,
+                    UUID.from_bytes(attribute_value),
+                    True
+                )
+
+                # Filter out returned services based on the given uuids list
+                if (not uuids) or (service.uuid in uuids):
+                    services.append(service)
+
+                # Add the service to the peer's service list
+                self.on_service_discovered(service)
+
+            # Stop if for some reason the list was empty
+            if not response.attributes:
+                break
+
+            # Move on to the next chunk
+            starting_handle = response.attributes[-1][1] + 1
+
+        return services
+
+    async def discover_service(self, uuid):
+        '''
+        See Vol 3, Part G - 4.4.2 Discover Primary Service by Service UUID
+        '''
+
+        # Force uuid to be a UUID object
+        if type(uuid) is str:
+            uuid = UUID(uuid)
+
+        starting_handle = 0x0001
+        services = []
+        while starting_handle < 0xFFFF:
+            response = await self.send_request(
+                ATT_Find_By_Type_Value_Request(
+                    starting_handle = starting_handle,
+                    ending_handle   = 0xFFFF,
+                    attribute_type  = GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
+                    attribute_value = uuid.to_pdu_bytes()
+                )
+            )
+            if response is None:
+                # TODO raise appropriate exception
+                return []
+
+            # Check if we reached the end of the iteration
+            if response.op_code == ATT_ERROR_RESPONSE:
+                if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
+                    # Unexpected end
+                    logger.waning(f'!!! unexpected error while discovering services: {HCI_Constant.error_name(response.error_code)}')
+                    # TODO raise appropriate exception
+                    return
+                break
+
+            for attribute_handle, end_group_handle in response.handles_information:
+                if attribute_handle < starting_handle or end_group_handle < attribute_handle:
+                    # Something's not right
+                    logger.warning(f'bogus handle values: {attribute_handle} {end_group_handle}')
+                    return
+
+                # Create a service proxy for this service
+                service = ServiceProxy(self, attribute_handle, end_group_handle, uuid, True)
+
+                # Add the service to the peer's service list
+                services.append(service)
+                self.on_service_discovered(service)
+
+                # Check if we've reached the end already
+                if end_group_handle == 0xFFFF:
+                    break
+
+            # Stop if for some reason the list was empty
+            if not response.handles_information:
+                break
+
+            # Move on to the next chunk
+            starting_handle = response.handles_information[-1][1] + 1
+
+        return services
+
+    async def discover_included_services(self, service):
+        '''
+        See Vol 3, Part G - 4.5.1 Find Included Services
+        '''
+        # TODO
+        return []
+
+    async def discover_characteristics(self, uuids, service):
+        '''
+        See Vol 3, Part G - 4.6.1 Discover All Characteristics of a Service and 4.6.2 Discover Characteristics by UUID
+        '''
+
+        # Cast the UUIDs type from string to object if needed
+        uuids = [UUID(uuid) if type(uuid) is str else uuid for uuid in uuids]
+
+        # Decide which services to discover for
+        services = [service] if service else self.services
+
+        # Perform characteristic discovery for each service
+        discovered_characteristics = []
+        for service in services:
+            starting_handle = service.handle
+            ending_handle   = service.end_group_handle
+
+            characteristics = []
+            while starting_handle <= ending_handle:
+                response = await self.send_request(
+                    ATT_Read_By_Type_Request(
+                        starting_handle = starting_handle,
+                        ending_handle   = ending_handle,
+                        attribute_type  = GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
+                    )
+                )
+                if response is None:
+                    # TODO raise appropriate exception
+                    return []
+
+                # Check if we reached the end of the iteration
+                if response.op_code == ATT_ERROR_RESPONSE:
+                    if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
+                        # Unexpected end
+                        logger.warning(f'!!! unexpected error while discovering characteristics: {HCI_Constant.error_name(response.error_code)}')
+                        # TODO raise appropriate exception
+                        return
+                    break
+
+                # Stop if for some reason the list was empty
+                if not response.attributes:
+                    break
+
+                # Process all characteristics returned in this iteration
+                for attribute_handle, attribute_value in response.attributes:
+                    if attribute_handle < starting_handle:
+                        # Something's not right
+                        logger.warning(f'bogus handle value: {attribute_handle}')
+                        return []
+
+                    properties, handle = struct.unpack_from('<BH', attribute_value)
+                    characteristic_uuid = UUID.from_bytes(attribute_value[3:])
+                    characteristic = CharacteristicProxy(self, handle, 0, characteristic_uuid, properties)
+
+                    # Set the previous characteristic's end handle
+                    if characteristics:
+                        characteristics[-1].end_group_handle = attribute_handle - 1
+
+                    characteristics.append(characteristic)
+
+                # Move on to the next characteristics
+                starting_handle = response.attributes[-1][0] + 1
+
+            # Set the end handle for the last characteristic
+            if characteristics:
+                characteristics[-1].end_group_handle = service.end_group_handle
+
+            # Set the service's characteristics
+            characteristics = [c for c in characteristics if not uuids or c.uuid in uuids]
+            service.characteristics = characteristics
+            discovered_characteristics.extend(characteristics)
+
+        return discovered_characteristics
+
+    async def discover_descriptors(self, characteristic = None, start_handle = None, end_handle = None):
+        '''
+        See Vol 3, Part G - 4.7.1 Discover All Characteristic Descriptors
+        '''
+        if characteristic:
+            starting_handle = characteristic.handle + 1
+            ending_handle   = characteristic.end_group_handle
+        elif start_handle and end_handle:
+            starting_handle = start_handle
+            ending_handle   = end_handle
+        else:
+            return []
+
+        descriptors = []
+        while starting_handle <= ending_handle:
+            response = await self.send_request(
+                ATT_Find_Information_Request(
+                    starting_handle = starting_handle,
+                    ending_handle   = ending_handle
+                )
+            )
+            if response is None:
+                # TODO raise appropriate exception
+                return []
+
+            # Check if we reached the end of the iteration
+            if response.op_code == ATT_ERROR_RESPONSE:
+                if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
+                    # Unexpected end
+                    logger.warning(f'!!! unexpected error while discovering descriptors: {HCI_Constant.error_name(response.error_code)}')
+                    # TODO raise appropriate exception
+                    return []
+                break
+
+            # Stop if for some reason the list was empty
+            if not response.information:
+                break
+
+            # Process all descriptors returned in this iteration
+            for attribute_handle, attribute_uuid in response.information:
+                if attribute_handle < starting_handle:
+                    # Something's not right
+                    logger.warning(f'bogus handle value: {attribute_handle}')
+                    return []
+
+                descriptor = DescriptorProxy(self, attribute_handle, UUID.from_bytes(attribute_uuid))
+                descriptors.append(descriptor)
+                # TODO: read descriptor value
+
+            # Move on to the next descriptor
+            starting_handle = response.information[-1][0] + 1
+
+        # Set the characteristic's descriptors
+        if characteristic:
+            characteristic.descriptors = descriptors
+
+        return descriptors
+
+    async def discover_attributes(self):
+        '''
+        Discover all attributes, regardless of type
+        '''
+        starting_handle = 0x0001
+        ending_handle   = 0xFFFF
+        attributes = []
+        while True:
+            response = await self.send_request(
+                ATT_Find_Information_Request(
+                    starting_handle = starting_handle,
+                    ending_handle   = ending_handle
+                )
+            )
+            if response is None:
+                return []
+
+            # Check if we reached the end of the iteration
+            if response.op_code == ATT_ERROR_RESPONSE:
+                if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
+                    # Unexpected end
+                    logger.warning(f'!!! unexpected error while discovering attributes: {HCI_Constant.error_name(response.error_code)}')
+                    return []
+                break
+
+            for attribute_handle, attribute_uuid in response.information:
+                if attribute_handle < starting_handle:
+                    # Something's not right
+                    logger.warning(f'bogus handle value: {attribute_handle}')
+                    return []
+
+                attribute = AttributeProxy(self, attribute_handle, 0, UUID.from_bytes(attribute_uuid))
+                attributes.append(attribute)
+
+            # Move on to the next attributes
+            starting_handle = attributes[-1].handle + 1
+
+        return attributes
+
+    async def subscribe(self, characteristic, subscriber=None):
+        # If we haven't already discovered the descriptors for this characteristic, do it now
+        if not characteristic.descriptors_discovered:
+            await self.discover_descriptors(characteristic)
+
+        # Look for the CCCD descriptor
+        cccd = characteristic.get_descriptor(GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR)
+        if not cccd:
+            logger.warning('subscribing to characteristic with no CCCD descriptor')
+            return
+
+        # Set the subscription bits and select the subscriber set
+        bits = 0
+        subscriber_sets = []
+        if characteristic.properties & Characteristic.NOTIFY:
+            bits |= 0x0001
+            subscriber_sets.append(self.notification_subscribers.setdefault(characteristic.handle, set()))
+        if characteristic.properties & Characteristic.INDICATE:
+            bits |= 0x0002
+            subscriber_sets.append(self.indication_subscribers.setdefault(characteristic.handle, set()))
+
+        # Add subscribers to the sets
+        for subscriber_set in subscriber_sets:
+            if subscriber is not None:
+                subscriber_set.add(subscriber)
+            subscriber_set.add(lambda value: characteristic.emit('update', self.connection, value))
+
+        await self.write_value(cccd, struct.pack('<H', bits), with_response=True)
+
+    async def read_value(self, attribute, no_long_read=False):
+        '''
+        See Vol 3, Part G - 4.8.1 Read Characteristic Value
+
+        `attribute` can be an Attribute object, or a handle value
+        '''
+
+        # Send a request to read
+        attribute_handle = attribute if type(attribute) is int else attribute.handle
+        response = await self.send_request(ATT_Read_Request(attribute_handle = attribute_handle))
+        if response is None:
+            raise TimeoutError('read timeout')
+        if response.op_code == ATT_ERROR_RESPONSE:
+            raise ProtocolError(
+                response.error_code,
+                'att',
+                ATT_PDU.error_name(response.error_code),
+                response
+            )
+
+        # If the value is the max size for the MTU, try to read more unless the caller
+        # specifically asked not to do that
+        attribute_value = response.attribute_value
+        if not no_long_read and len(attribute_value) == self.mtu - 1:
+            logger.debug('using READ BLOB to get the rest of the value')
+            offset = len(attribute_value)
+            while True:
+                response = await self.send_request(
+                    ATT_Read_Blob_Request(attribute_handle = attribute_handle, value_offset = offset)
+                )
+                if response is None:
+                    raise TimeoutError('read timeout')
+                if response.op_code == ATT_ERROR_RESPONSE:
+                    if response.error_code == ATT_ATTRIBUTE_NOT_LONG_ERROR or response.error_code == ATT_INVALID_OFFSET_ERROR:
+                        break
+                    raise ProtocolError(
+                        response.error_code,
+                        'att',
+                        ATT_PDU.error_name(response.error_code),
+                        response
+                    )
+
+                part = response.part_attribute_value
+                attribute_value += part
+
+                if len(part) < self.mtu - 1:
+                    break
+
+                offset += len(part)
+
+        # Return the value as bytes
+        return attribute_value
+
+    async def read_characteristics_by_uuid(self, uuid, service):
+        '''
+        See Vol 3, Part G - 4.8.2 Read Using Characteristic UUID
+        '''
+
+        if service is None:
+            starting_handle = 0x0001
+            ending_handle   = 0xFFFF
+        else:
+            starting_handle = service.handle
+            ending_handle   = service.end_group_handle
+
+        characteristics_values = []
+        while starting_handle <= ending_handle:
+            response = await self.send_request(
+                ATT_Read_By_Type_Request(
+                    starting_handle = starting_handle,
+                    ending_handle   = ending_handle,
+                    attribute_type  = uuid
+                )
+            )
+            if response is None:
+                # TODO raise appropriate exception
+                return []
+
+            # Check if we reached the end of the iteration
+            if response.op_code == ATT_ERROR_RESPONSE:
+                if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR:
+                    # Unexpected end
+                    logger.warning(f'!!! unexpected error while reading characteristics: {HCI_Constant.error_name(response.error_code)}')
+                    # TODO raise appropriate exception
+                    return []
+                break
+
+            # Stop if for some reason the list was empty
+            if not response.attributes:
+                break
+
+            # Process all characteristics returned in this iteration
+            for attribute_handle, attribute_value in response.attributes:
+                if attribute_handle < starting_handle:
+                    # Something's not right
+                    logger.warning(f'bogus handle value: {attribute_handle}')
+                    return []
+
+                characteristics_values.append(attribute_value)
+
+            # Move on to the next characteristics
+            starting_handle = response.attributes[-1][0] + 1
+
+        return characteristics_values
+
+    async def write_value(self, attribute, value, with_response=False):
+        '''
+        See Vol 3, Part G - 4.9.1 Write Without Response & 4.9.3 Write Characteristic Value
+
+        `attribute` can be an Attribute object, or a handle value
+        '''
+
+        # Send a request or command to write
+        attribute_handle = attribute if type(attribute) is int else attribute.handle
+        if with_response:
+            response = await self.send_request(
+                ATT_Write_Request(
+                    attribute_handle = attribute_handle,
+                    attribute_value  = value
+                )
+            )
+            if response.op_code == ATT_ERROR_RESPONSE:
+                raise ProtocolError(
+                    response.error_code,
+                    'att',
+                    ATT_PDU.error_name(response.error_code), response
+                )
+        else:
+            await self.send_command(
+                ATT_Write_Command(
+                    attribute_handle = attribute_handle,
+                    attribute_value  = value
+                )
+            )
+
+    def on_gatt_pdu(self, att_pdu):
+        logger.debug(f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}')
+        if att_pdu.op_code in ATT_RESPONSES:
+            if self.pending_request is None:
+                # Not expected!
+                logger.warning('!!! unexpected response, there is no pending request')
+                return
+
+            # Sanity check: the response should match the pending request unless it is an error response
+            if att_pdu.op_code != ATT_ERROR_RESPONSE:
+                expected_response_name = self.pending_request.name.replace('_REQUEST', '_RESPONSE')
+                if att_pdu.name != expected_response_name:
+                    logger.warning(f'!!! mismatched response: expected {expected_response_name}')
+                    return
+
+            # Return the response to the coroutine that is waiting for it
+            self.pending_response.set_result(att_pdu)
+        else:
+            handler_name = f'on_{att_pdu.name.lower()}'
+            handler = getattr(self, handler_name, None)
+            if handler is not None:
+                handler(att_pdu)
+            else:
+                logger.warning(f'{color(f"--- Ignoring GATT Response from [0x{self.connection.handle:04X}]:", "red")} {att_pdu}')
+
+    def on_att_handle_value_notification(self, notification):
+        # Call all subscribers
+        subscribers = self.notification_subscribers.get(notification.attribute_handle, [])
+        if not subscribers:
+            logger.warning('!!! received notification with no subscriber')
+        for subscriber in subscribers:
+            subscriber(notification.attribute_value)
+
+    def on_att_handle_value_indication(self, indication):
+        # Call all subscribers
+        subscribers = self.indication_subscribers.get(indication.attribute_handle, [])
+        if not subscribers:
+            logger.warning('!!! received indication with no subscriber')
+        for subscriber in subscribers:
+            subscriber(indication.attribute_value)
+
+        # Confirm that we received the indication
+        self.send_confirmation(ATT_Handle_Value_Confirmation())
diff --git a/bumble/gatt_server.py b/bumble/gatt_server.py
new file mode 100644
index 0000000..3afcecc
--- /dev/null
+++ b/bumble/gatt_server.py
@@ -0,0 +1,698 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# GATT - Generic Attribute Profile
+# Server
+#
+# See Bluetooth spec @ Vol 3, Part G
+#
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+from collections import defaultdict
+from pyee import EventEmitter
+from colors import color
+
+from .core import *
+from .hci import *
+from .att import *
+from .gatt import *
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# GATT Server
+# -----------------------------------------------------------------------------
+class Server(EventEmitter):
+    def __init__(self, device):
+        super().__init__()
+        self.device                = device
+        self.attributes            = []  # Attributes, ordered by increasing handle values
+        self.attributes_by_handle  = {}  # Map for fast attribute access by handle
+        self.max_mtu               = 23  # FIXME: 517  # The max MTU we're willing to negotiate
+        self.subscribers           = {}  # Map of subscriber states by connection handle and attribute handle
+        self.mtus                  = {}  # Map of ATT MTU values by connection handle
+        self.indication_semaphores = defaultdict(lambda: asyncio.Semaphore(1))
+        self.pending_confirmations = defaultdict(lambda: None)
+
+    def send_gatt_pdu(self, connection_handle, pdu):
+        self.device.send_l2cap_pdu(connection_handle, ATT_CID, pdu)
+
+    def next_handle(self):
+        return 1 + len(self.attributes)
+
+    def get_attribute(self, handle):
+        attribute = self.attributes_by_handle.get(handle)
+        if attribute:
+            return attribute
+
+        # Not in the cached map, perform a linear lookup
+        for attribute in self.attributes:
+            if attribute.handle == handle:
+                # Store in cached map
+                self.attributes_by_handle[handle] = attribute
+                return attribute
+        return None
+
+    def add_attribute(self, attribute):
+        # Assign a handle to this attribute
+        attribute.handle = self.next_handle()
+        attribute.end_group_handle = attribute.handle  # TODO: keep track of descriptors in the group
+
+        # Add this attribute to the list
+        self.attributes.append(attribute)
+
+    def add_service(self, service):
+        # Add the service attribute to the DB
+        self.add_attribute(service)
+
+        # TODO: add included services
+
+        # Add all characteristics
+        for characteristic in service.characteristics:
+            # Add a Characteristic Declaration (Vol 3, Part G - 3.3.1 Characteristic Declaration)
+            declaration_bytes = struct.pack(
+                '<BH',
+                characteristic.properties,
+                self.next_handle() + 1,  # The value will be the next attribute after this declaration
+            ) + characteristic.uuid.to_pdu_bytes()
+            characteristic_declaration = Attribute(
+                GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
+                Attribute.READABLE,
+                declaration_bytes
+            )
+            self.add_attribute(characteristic_declaration)
+
+            # Add the characteristic value
+            self.add_attribute(characteristic)
+
+            # Add the descriptors
+            for descriptor in characteristic.descriptors:
+                self.add_attribute(descriptor)
+
+            # If the characteristic supports subscriptions, add a CCCD descriptor
+            # unless there is one already
+            if (
+                characteristic.properties & (Characteristic.NOTIFY | Characteristic.INDICATE) and
+                characteristic.get_descriptor(GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR) is None
+            ):
+                self.add_attribute(
+                    Descriptor(
+                        GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
+                        Attribute.READABLE | Attribute.WRITEABLE,
+                        CharacteristicValue(
+                            read=lambda connection, characteristic=characteristic: self.read_cccd(connection, characteristic),
+                            write=lambda connection, value, characteristic=characteristic: self.write_cccd(connection, characteristic, value)
+                        )
+                    )
+                )
+
+            # Update the service and characteristic group ends
+            characteristic_declaration.end_group_handle = self.attributes[-1].handle
+            characteristic.end_group_handle = self.attributes[-1].handle
+
+        # Update the service group end
+        service.end_group_handle = self.attributes[-1].handle
+
+    def add_services(self, services):
+        for service in services:
+            self.add_service(service)
+
+    def read_cccd(self, connection, characteristic):
+        if connection is None:
+            return bytes([0, 0])
+
+        subscribers = self.subscribers.get(connection.handle)
+        cccd = None
+        if subscribers:
+            cccd = subscribers.get(characteristic.handle)
+
+        return cccd or bytes([0, 0])
+
+    def write_cccd(self, connection, characteristic, value):
+        logger.debug(f'Subscription update for connection={connection.handle:04X}, handle={characteristic.handle:04X}: {value.hex()}')
+
+        # Sanity check
+        if len(value) != 2:
+            logger.warn('CCCD value not 2 bytes long')
+            return
+
+        cccds = self.subscribers.setdefault(connection.handle, {})
+        cccds[characteristic.handle] = value
+        logger.debug(f'CCCDs: {cccds}')
+        notify_enabled = (value[0] & 0x01 != 0)
+        indicate_enabled = (value[0] & 0x02 != 0)
+        characteristic.emit('subscription', connection, notify_enabled, indicate_enabled)
+        self.emit('characteristic_subscription', connection, characteristic, notify_enabled, indicate_enabled)
+
+    def send_response(self, connection, response):
+        logger.debug(f'GATT Response from server: [0x{connection.handle:04X}] {response}')
+        self.send_gatt_pdu(connection.handle, response.to_bytes())
+
+    async def notify_subscriber(self, connection, attribute, force=False):
+        # Check if there's a subscriber
+        if not force:
+            subscribers = self.subscribers.get(connection.handle)
+            if not subscribers:
+                logger.debug('not notifying, no subscribers')
+                return
+            cccd = subscribers.get(attribute.handle)
+            if not cccd:
+                logger.debug(f'not notifying, no subscribers for handle {attribute.handle:04X}')
+                return
+            if len(cccd) != 2 or (cccd[0] & 0x01 == 0):
+                logger.debug(f'not notifying, cccd={cccd.hex()}')
+                return
+
+        # Get the value
+        value = attribute.read_value(connection)
+
+        # Truncate if needed
+        mtu = self.get_mtu(connection)
+        if len(value) > mtu - 3:
+            value = value[:mtu - 3]
+
+        # Notify
+        notification = ATT_Handle_Value_Notification(
+            attribute_handle = attribute.handle,
+            attribute_value  = value
+        )
+        logger.debug(f'GATT Notify from server: [0x{connection.handle:04X}] {notification}')
+        self.send_gatt_pdu(connection.handle, notification.to_bytes())
+
+    async def notify_subscribers(self, attribute, force=False):
+        # Get all the connections for which there's at least one subscription
+        connections = [
+            connection for connection in [
+                self.device.lookup_connection(connection_handle)
+                for (connection_handle, subscribers) in self.subscribers.items()
+                if force or subscribers.get(attribute.handle)
+            ]
+            if connection is not None
+        ]
+
+        # Notify for each connection
+        if connections:
+            await asyncio.wait([
+                self.notify_subscriber(connection, attribute, force)
+                for connection in connections
+            ])
+
+    async def indicate_subscriber(self, connection, attribute, force=False):
+        # Check if there's a subscriber
+        if not force:
+            subscribers = self.subscribers.get(connection.handle)
+            if not subscribers:
+                logger.debug('not indicating, no subscribers')
+                return
+            cccd = subscribers.get(attribute.handle)
+            if not cccd:
+                logger.debug(f'not indicating, no subscribers for handle {attribute.handle:04X}')
+                return
+            if len(cccd) != 2 or (cccd[0] & 0x02 == 0):
+                logger.debug(f'not indicating, cccd={cccd.hex()}')
+                return
+
+        # Get the value
+        value = attribute.read_value(connection)
+
+        # Truncate if needed
+        mtu = self.get_mtu(connection)
+        if len(value) > mtu - 3:
+            value = value[:mtu - 3]
+
+        # Indicate
+        indication = ATT_Handle_Value_Indication(
+            attribute_handle = attribute.handle,
+            attribute_value  = value
+        )
+        logger.debug(f'GATT Indicate from server: [0x{connection.handle:04X}] {indication}')
+
+        # Wait until we can send (only one pending indication at a time per connection)
+        async with self.indication_semaphores[connection.handle]:
+            assert(self.pending_confirmations[connection.handle] is None)
+
+            # Create a future value to hold the eventual response
+            self.pending_confirmations[connection.handle] = asyncio.get_running_loop().create_future()
+
+            try:
+                self.send_gatt_pdu(connection.handle, indication.to_bytes())
+                await asyncio.wait_for(self.pending_confirmations[connection.handle], GATT_REQUEST_TIMEOUT)
+            except asyncio.TimeoutError:
+                logger.warning(color('!!! GATT Indicate timeout', 'red'))
+                raise TimeoutError(f'GATT timeout for {indication.name}')
+            finally:
+                self.pending_confirmations[connection.handle] = None
+
+    async def indicate_subscribers(self, attribute):
+        # Get all the connections for which there's at least one subscription
+        connections = [
+            connection for connection in [
+                self.device.lookup_connection(connection_handle)
+                for (connection_handle, subscribers) in self.subscribers.items()
+                if subscribers.get(attribute.handle)
+            ]
+            if connection is not None
+        ]
+
+        # Indicate for each connection
+        if connections:
+            await asyncio.wait([
+                self.indicate_subscriber(connection, attribute)
+                for connection in connections
+            ])
+
+    def on_disconnection(self, connection):
+        if connection.handle in self.mtus:
+            del self.mtus[connection.handle]
+        if connection.handle in self.subscribers:
+            del self.subscribers[connection.handle]
+        if connection.handle in self.indication_semaphores:
+            del self.indication_semaphores[connection.handle]
+        if connection.handle in self.pending_confirmations:
+            del self.pending_confirmations[connection.handle]
+
+    def on_gatt_pdu(self, connection, att_pdu):
+        logger.debug(f'GATT Request to server: [0x{connection.handle:04X}] {att_pdu}')
+        handler_name = f'on_{att_pdu.name.lower()}'
+        handler = getattr(self, handler_name, None)
+        if handler is not None:
+            try:
+                handler(connection, att_pdu)
+            except ATT_Error as error:
+                logger.debug(f'normal exception returned by handler: {error}')
+                response = ATT_Error_Response(
+                    request_opcode_in_error   = att_pdu.op_code,
+                    attribute_handle_in_error = error.att_handle,
+                    error_code                = error.error_code
+                )
+                self.send_response(connection, response)
+            except Exception as error:
+                logger.warning(f'{color("!!! Exception in handler:", "red")} {error}')
+                response = ATT_Error_Response(
+                    request_opcode_in_error   = att_pdu.op_code,
+                    attribute_handle_in_error = 0x0000,
+                    error_code                = ATT_UNLIKELY_ERROR_ERROR
+                )
+                self.send_response(connection, response)
+                raise error
+        else:
+            # No specific handler registered
+            if att_pdu.op_code in ATT_REQUESTS:
+                # Invoke the generic handler
+                self.on_att_request(connection, att_pdu)
+            else:
+                # Just ignore
+                logger.warning(f'{color("--- Ignoring GATT Request from [0x{connection.handle:04X}]:", "red")}  {att_pdu}')
+
+    def get_mtu(self, connection):
+        return self.mtus.get(connection.handle, ATT_DEFAULT_MTU)
+
+    #######################################################
+    # ATT handlers
+    #######################################################
+    def on_att_request(self, connection, pdu):
+        '''
+        Handler for requests without a more specific handler
+        '''
+        logger.warning(f'{color(f"--- Unsupported ATT Request from [0x{connection.handle:04X}]:", "red")} {pdu}')
+        response = ATT_Error_Response(
+            request_opcode_in_error   = pdu.op_code,
+            attribute_handle_in_error = 0x0000,
+            error_code                = ATT_REQUEST_NOT_SUPPORTED_ERROR
+        )
+        self.send_response(connection, response)
+
+    def on_att_exchange_mtu_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.2.1 Exchange MTU Request
+        '''
+        mtu = max(ATT_DEFAULT_MTU, min(self.max_mtu, request.client_rx_mtu))
+        self.mtus[connection.handle] = mtu
+        self.send_response(connection, ATT_Exchange_MTU_Response(server_rx_mtu = mtu))
+
+        # Notify the device
+        self.device.on_connection_att_mtu_update(connection.handle, mtu)
+
+    def on_att_find_information_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.3.1 Find Information Request
+        '''
+
+        # Check the request parameters
+        if request.starting_handle == 0 or request.starting_handle > request.ending_handle:
+            self.send_response(connection, ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.starting_handle,
+                error_code                = ATT_INVALID_HANDLE_ERROR
+            ))
+            return
+
+        # Build list of returned attributes
+        pdu_space_available = self.get_mtu(connection) - 2
+        attributes = []
+        uuid_size = 0
+        for attribute in (
+            attribute for attribute in self.attributes if
+            attribute.handle >= request.starting_handle and
+            attribute.handle <= request.ending_handle
+        ):
+            # TODO: check permissions
+
+            this_uuid_size = len(attribute.type.to_pdu_bytes())
+
+            if attributes:
+                # Check if this attribute has the same type size as the previous one
+                if this_uuid_size != uuid_size:
+                    break
+
+            # Check if there's enough space for one more entry
+            uuid_size = this_uuid_size
+            if pdu_space_available < 2 + uuid_size:
+                break
+
+            # Add the attribute to the list
+            attributes.append(attribute)
+            pdu_space_available -= 2 + uuid_size
+
+        # Return the list of attributes
+        if attributes:
+            information_data_list = [
+                struct.pack('<H', attribute.handle) + attribute.type.to_pdu_bytes()
+                for attribute in attributes
+            ]
+            response = ATT_Find_Information_Response(
+                format           = 1 if len(attributes[0].type.to_pdu_bytes()) == 2 else 2,
+                information_data = b''.join(information_data_list)
+            )
+        else:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.starting_handle,
+                error_code                = ATT_ATTRIBUTE_NOT_FOUND_ERROR
+            )
+
+        self.send_response(connection, response)
+
+    def on_att_find_by_type_value_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.3.3 Find By Type Value Request
+        '''
+
+        # Build list of returned attributes
+        pdu_space_available = self.get_mtu(connection) - 2
+        attributes = []
+        for attribute in (
+            attribute for attribute in self.attributes if
+            attribute.handle                 >= request.starting_handle and
+            attribute.handle                 <= request.ending_handle   and
+            attribute.type                   == request.attribute_type  and
+            attribute.read_value(connection) == request.attribute_value and
+            pdu_space_available >= 4
+        ):
+            # TODO: check permissions
+
+            # Add the attribute to the list
+            attributes.append(attribute)
+            pdu_space_available -= 4
+
+        # Return the list of attributes
+        if attributes:
+            handles_information_list = []
+            for attribute in attributes:
+                if attribute.type in {
+                    GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
+                    GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
+                    GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
+                }:
+                    # Part of a group
+                    group_end_handle = attribute.end_group_handle
+                else:
+                    # Not part of a group
+                    group_end_handle = attribute.handle
+                handles_information_list.append(struct.pack('<HH', attribute.handle, group_end_handle))
+            response = ATT_Find_By_Type_Value_Response(
+                handles_information_list = b''.join(handles_information_list)
+            )
+        else:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.starting_handle,
+                error_code                = ATT_ATTRIBUTE_NOT_FOUND_ERROR
+            )
+
+        self.send_response(connection, response)
+
+    def on_att_read_by_type_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.4.1 Read By Type Request
+        '''
+
+        mtu = self.get_mtu(connection)
+        pdu_space_available = mtu - 2
+        attributes = []
+        for attribute in (
+            attribute for attribute in self.attributes if
+            attribute.type   == request.attribute_type  and
+            attribute.handle >= request.starting_handle and
+            attribute.handle <= request.ending_handle   and
+            pdu_space_available
+        ):
+            # TODO: check permissions
+
+            # Check the attribute value size
+            attribute_value = attribute.read_value(connection)
+            max_attribute_size = min(mtu - 4, 253)
+            if len(attribute_value) > max_attribute_size:
+                # We need to truncate
+                attribute_value = attribute_value[:max_attribute_size]
+            if attributes and len(attributes[0][1]) != len(attribute_value):
+                # Not the same size as previous attribute, stop here
+                break
+
+            # Check if there is enough space
+            entry_size = 2 + len(attribute_value)
+            if pdu_space_available < entry_size:
+                break
+
+            # Add the attribute to the list
+            attributes.append((attribute.handle, attribute_value))
+            pdu_space_available -= entry_size
+
+        if attributes:
+            attribute_data_list = [struct.pack('<H', handle) + value for handle, value in attributes]
+            response = ATT_Read_By_Type_Response(
+                length              = entry_size,
+                attribute_data_list = b''.join(attribute_data_list)
+            )
+        else:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.starting_handle,
+                error_code                = ATT_ATTRIBUTE_NOT_FOUND_ERROR
+            )
+
+        self.send_response(connection, response)
+
+    def on_att_read_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.4.3 Read Request
+        '''
+
+        if attribute := self.get_attribute(request.attribute_handle):
+            # TODO: check permissions
+            value = attribute.read_value(connection)
+            value_size = min(self.get_mtu(connection) - 1, len(value))
+            response = ATT_Read_Response(
+                attribute_value = value[:value_size]
+            )
+        else:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.attribute_handle,
+                error_code                = ATT_INVALID_HANDLE_ERROR
+            )
+        self.send_response(connection, response)
+
+    def on_att_read_blob_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.4.5 Read Blob Request
+        '''
+
+        if attribute := self.get_attribute(request.attribute_handle):
+            # TODO: check permissions
+            mtu = self.get_mtu(connection)
+            value = attribute.read_value(connection)
+            if request.value_offset > len(value):
+                response = ATT_Error_Response(
+                    request_opcode_in_error = request.op_code,
+                    attribute_handle_in_error = request.attribute_handle,
+                    error_code                = ATT_INVALID_OFFSET_ERROR
+                )
+            elif len(value) <= mtu - 1:
+                response = ATT_Error_Response(
+                    request_opcode_in_error = request.op_code,
+                    attribute_handle_in_error = request.attribute_handle,
+                    error_code                = ATT_ATTRIBUTE_NOT_LONG_ERROR
+                )
+            else:
+                part_size = min(mtu - 1, len(value) - request.value_offset)
+                response = ATT_Read_Blob_Response(
+                    part_attribute_value = value[request.value_offset:request.value_offset + part_size]
+                )
+        else:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.attribute_handle,
+                error_code                = ATT_INVALID_HANDLE_ERROR
+            )
+        self.send_response(connection, response)
+
+    def on_att_read_by_group_type_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.4.9 Read by Group Type Request
+        '''
+        if request.attribute_group_type not in {
+            GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE,
+            GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
+            GATT_INCLUDE_ATTRIBUTE_TYPE
+        }:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.starting_handle,
+                error_code                = ATT_UNSUPPORTED_GROUP_TYPE_ERROR
+            )
+            self.send_response(connection, response)
+            return
+
+        mtu = self.get_mtu(connection)
+        pdu_space_available = mtu - 2
+        attributes = []
+        for attribute in (
+            attribute for attribute in self.attributes if
+            attribute.type   == request.attribute_group_type and
+            attribute.handle >= request.starting_handle      and
+            attribute.handle <= request.ending_handle        and
+            pdu_space_available
+        ):
+            # Check the attribute value size
+            attribute_value = attribute.read_value(connection)
+            max_attribute_size = min(mtu - 6, 251)
+            if len(attribute_value) > max_attribute_size:
+                # We need to truncate
+                attribute_value = attribute_value[:max_attribute_size]
+            if attributes and len(attributes[0][2]) != len(attribute_value):
+                # Not the same size as previous attributes, stop here
+                break
+
+            # Check if there is enough space
+            entry_size = 4 + len(attribute_value)
+            if pdu_space_available < entry_size:
+                break
+
+            # Add the attribute to the list
+            attributes.append((attribute.handle, attribute.end_group_handle, attribute_value))
+            pdu_space_available -= entry_size
+
+        if attributes:
+            attribute_data_list = [
+                struct.pack('<HH', handle, end_group_handle) + value
+                for handle, end_group_handle, value in attributes
+            ]
+            response = ATT_Read_By_Group_Type_Response(
+                length              = len(attribute_data_list[0]),
+                attribute_data_list = b''.join(attribute_data_list)
+            )
+        else:
+            response = ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.starting_handle,
+                error_code                = ATT_ATTRIBUTE_NOT_FOUND_ERROR
+            )
+
+        self.send_response(connection, response)
+
+    def on_att_write_request(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.5.1 Write Request
+        '''
+
+        # Check  that the attribute exists
+        attribute = self.get_attribute(request.attribute_handle)
+        if attribute is None:
+            self.send_response(connection, ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.attribute_handle,
+                error_code                = ATT_INVALID_HANDLE_ERROR
+            ))
+            return
+
+        # TODO: check permissions
+
+        # Check the request parameters
+        if len(request.attribute_value) > GATT_MAX_ATTRIBUTE_VALUE_SIZE:
+            self.send_response(connection, ATT_Error_Response(
+                request_opcode_in_error   = request.op_code,
+                attribute_handle_in_error = request.attribute_handle,
+                error_code                = ATT_INVALID_ATTRIBUTE_LENGTH_ERROR
+            ))
+            return
+
+        # Accept the value
+        attribute.write_value(connection, request.attribute_value)
+
+        # Done
+        self.send_response(connection, ATT_Write_Response())
+
+    def on_att_write_command(self, connection, request):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.5.3 Write Command
+        '''
+
+        # Check that the attribute exists
+        attribute = self.get_attribute(request.attribute_handle)
+        if attribute is None:
+            return
+
+        # TODO: check permissions
+
+        # Check the request parameters
+        if len(request.attribute_value) > GATT_MAX_ATTRIBUTE_VALUE_SIZE:
+            return
+
+        # Accept the value
+        try:
+            attribute.write_value(connection, request.attribute_value)
+        except Exception as error:
+            logger.warning(f'!!! ignoring exception: {error}')
+
+    def on_att_handle_value_confirmation(self, connection, confirmation):
+        '''
+        See Bluetooth spec Vol 3, Part F - 3.4.7.3 Handle Value Confirmation
+        '''
+        if self.pending_confirmations[connection.handle] is None:
+            # Not expected!
+            logger.warning('!!! unexpected confirmation, there is no pending indication')
+            return
+
+        self.pending_confirmations[connection.handle].set_result(None)
diff --git a/bumble/hci.py b/bumble/hci.py
new file mode 100644
index 0000000..8358e30
--- /dev/null
+++ b/bumble/hci.py
@@ -0,0 +1,3540 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import struct
+import collections
+import logging
+import functools
+from colors import color
+
+from .core import *
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+def hci_command_op_code(ogf, ocf):
+    return (ogf << 10 | ocf)
+
+
+def key_with_value(dictionary, target_value):
+    for key, value in dictionary.items():
+        if value == target_value:
+            return key
+    return None
+
+
+def indent_lines(str):
+    return '\n'.join(['  ' + line for line in str.split('\n')])
+
+
+def map_null_terminated_utf8_string(utf8_bytes):
+    try:
+        terminator = utf8_bytes.find(0)
+        if terminator < 0:
+            return utf8_bytes
+        return utf8_bytes[0:terminator].decode('utf8')
+    except UnicodeDecodeError:
+        return utf8_bytes
+
+
+def map_class_of_device(class_of_device):
+    service_classes, major_device_class, minor_device_class = DeviceClass.split_class_of_device(class_of_device)
+    return f'[{class_of_device:06X}] Services({",".join(DeviceClass.service_class_labels(service_classes))}),Class({DeviceClass.major_device_class_name(major_device_class)}|{DeviceClass.minor_device_class_name(major_device_class, minor_device_class)})'
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+
+# HCI Version
+HCI_VERSION_BLUETOOTH_CORE_1_0B    = 0
+HCI_VERSION_BLUETOOTH_CORE_1_1     = 1
+HCI_VERSION_BLUETOOTH_CORE_1_2     = 2
+HCI_VERSION_BLUETOOTH_CORE_2_0_EDR = 3
+HCI_VERSION_BLUETOOTH_CORE_2_1_EDR = 4
+HCI_VERSION_BLUETOOTH_CORE_3_0_HS  = 5
+HCI_VERSION_BLUETOOTH_CORE_4_0     = 6
+HCI_VERSION_BLUETOOTH_CORE_4_1     = 7
+HCI_VERSION_BLUETOOTH_CORE_4_2     = 8
+HCI_VERSION_BLUETOOTH_CORE_5_0     = 9
+HCI_VERSION_BLUETOOTH_CORE_5_1     = 10
+HCI_VERSION_BLUETOOTH_CORE_5_2     = 11
+HCI_VERSION_BLUETOOTH_CORE_5_3     = 12
+
+# HCI Packet types
+HCI_COMMAND_PACKET          = 0x01
+HCI_ACL_DATA_PACKET         = 0x02
+HCI_SYNCHRONOUS_DATA_PACKET = 0x03
+HCI_EVENT_PACKET            = 0x04
+
+# HCI Event Codes
+HCI_INQUIRY_COMPLETE_EVENT                            = 0x01
+HCI_INQUIRY_RESULT_EVENT                              = 0x02
+HCI_CONNECTION_COMPLETE_EVENT                         = 0x03
+HCI_CONNECTION_REQUEST_EVENT                          = 0x04
+HCI_DISCONNECTION_COMPLETE_EVENT                      = 0x05
+HCI_AUTHENTICATION_COMPLETE_EVENT                     = 0x06
+HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT                = 0x07
+HCI_ENCRYPTION_CHANGE_EVENT                           = 0x08
+HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT         = 0x09
+HCI_LINK_KEY_TYPE_CHANGED_EVENT                       = 0x0A
+HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT     = 0x0B
+HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT    = 0x0C
+HCI_QOS_SETUP_COMPLETE_EVENT                          = 0x0D
+HCI_COMMAND_COMPLETE_EVENT                            = 0x0E
+HCI_COMMAND_STATUS_EVENT                              = 0x0F
+HCI_HARDWARE_ERROR_EVENT                              = 0x10
+HCI_FLUSH_OCCURRED_EVENT                              = 0x11
+HCI_ROLE_CHANGE_EVENT                                 = 0x12
+HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT                 = 0x13
+HCI_MODE_CHANGE_EVENT                                 = 0x14
+HCI_RETURN_LINK_KEYS_EVENT                            = 0x15
+HCI_PIN_CODE_REQUEST_EVENT                            = 0x16
+HCI_LINK_KEY_REQUEST_EVENT                            = 0x17
+HCI_LINK_KEY_NOTIFICATION_EVENT                       = 0x18
+HCI_LOOPBACK_COMMAND_EVENT                            = 0x19
+HCI_DATA_BUFFER_OVERFLOW_EVENT                        = 0x1A
+HCI_MAX_SLOTS_CHANGE_EVENT                            = 0x1B
+HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT                  = 0x1C
+HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT              = 0x1D
+HCI_QOS_VIOLATION_EVENT                               = 0x1E
+HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT            = 0x20
+HCI_FLOW_SPECIFICATION_COMPLETE_EVENT                 = 0x21
+HCI_INQUIRY_RESULT_WITH_RSSI_EVENT                    = 0x22
+HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT      = 0x23
+HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT             = 0x2C
+HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT              = 0x2D
+HCI_SNIFF_SUBRATING_EVENT                             = 0x2E
+HCI_EXTENDED_INQUIRY_RESULT_EVENT                     = 0x2F
+HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT             = 0x30
+HCI_IO_CAPABILITY_REQUEST_EVENT                       = 0x31
+HCI_IO_CAPABILITY_RESPONSE_EVENT                      = 0x32
+HCI_USER_CONFIRMATION_REQUEST_EVENT                   = 0x33
+HCI_USER_PASSKEY_REQUEST_EVENT                        = 0x34
+HCI_REMOTE_OOB_DATA_REQUEST                           = 0x35
+HCI_SIMPLE_PAIRING_COMPLETE_EVENT                     = 0x36
+HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT            = 0x38
+HCI_ENHANCED_FLUSH_COMPLETE_EVENT                     = 0x39
+HCI_USER_PASSKEY_NOTIFICATION_EVENT                   = 0x3B
+HCI_KEYPRESS_NOTIFICATION_EVENT                       = 0x3C
+HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT = 0x3D
+HCI_LE_META_EVENT                                     = 0x3E
+HCI_NUMBER_OF_COMPLETED_DATA_BLOCKS_EVENT             = 0x48
+
+HCI_EVENT_NAMES = {
+    HCI_INQUIRY_COMPLETE_EVENT:                            'HCI_INQUIRY_COMPLETE_EVENT',
+    HCI_INQUIRY_RESULT_EVENT:                              'HCI_INQUIRY_RESULT_EVENT',
+    HCI_CONNECTION_COMPLETE_EVENT:                         'HCI_CONNECTION_COMPLETE_EVENT',
+    HCI_CONNECTION_REQUEST_EVENT:                          'HCI_CONNECTION_REQUEST_EVENT',
+    HCI_DISCONNECTION_COMPLETE_EVENT:                      'HCI_DISCONNECTION_COMPLETE_EVENT',
+    HCI_AUTHENTICATION_COMPLETE_EVENT:                     'HCI_AUTHENTICATION_COMPLETE_EVENT',
+    HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT:                'HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT',
+    HCI_ENCRYPTION_CHANGE_EVENT:                           'HCI_ENCRYPTION_CHANGE_EVENT',
+    HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT:         'HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT',
+    HCI_LINK_KEY_TYPE_CHANGED_EVENT:                       'HCI_LINK_KEY_TYPE_CHANGED_EVENT',
+    HCI_INQUIRY_RESULT_WITH_RSSI_EVENT:                    'HCI_INQUIRY_RESULT_WITH_RSSI_EVENT',
+    HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT:     'HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT',
+    HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT:    'HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT',
+    HCI_QOS_SETUP_COMPLETE_EVENT:                          'HCI_QOS_SETUP_COMPLETE_EVENT',
+    HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT:             'HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT',
+    HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT:              'HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT',
+    HCI_SNIFF_SUBRATING_EVENT:                             'HCI_SNIFF_SUBRATING_EVENT',
+    HCI_COMMAND_COMPLETE_EVENT:                            'HCI_COMMAND_COMPLETE_EVENT',
+    HCI_COMMAND_STATUS_EVENT:                              'HCI_COMMAND_STATUS_EVENT',
+    HCI_HARDWARE_ERROR_EVENT:                              'HCI_HARDWARE_ERROR_EVENT',
+    HCI_FLUSH_OCCURRED_EVENT:                              'HCI_FLUSH_OCCURRED_EVENT',
+    HCI_ROLE_CHANGE_EVENT:                                 'HCI_ROLE_CHANGE_EVENT',
+    HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:                 'HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT',
+    HCI_MODE_CHANGE_EVENT:                                 'HCI_MODE_CHANGE_EVENT',
+    HCI_RETURN_LINK_KEYS_EVENT:                            'HCI_RETURN_LINK_KEYS_EVENT',
+    HCI_PIN_CODE_REQUEST_EVENT:                            'HCI_PIN_CODE_REQUEST_EVENT',
+    HCI_LINK_KEY_REQUEST_EVENT:                            'HCI_LINK_KEY_REQUEST_EVENT',
+    HCI_LINK_KEY_NOTIFICATION_EVENT:                       'HCI_LINK_KEY_NOTIFICATION_EVENT',
+    HCI_LOOPBACK_COMMAND_EVENT:                            'HCI_LOOPBACK_COMMAND_EVENT',
+    HCI_DATA_BUFFER_OVERFLOW_EVENT:                        'HCI_DATA_BUFFER_OVERFLOW_EVENT',
+    HCI_MAX_SLOTS_CHANGE_EVENT:                            'HCI_MAX_SLOTS_CHANGE_EVENT',
+    HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT:                  'HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT',
+    HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT:              'HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT',
+    HCI_QOS_VIOLATION_EVENT:                               'HCI_QOS_VIOLATION_EVENT',
+    HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT:            'HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT',
+    HCI_FLOW_SPECIFICATION_COMPLETE_EVENT:                 'HCI_FLOW_SPECIFICATION_COMPLETE_EVENT',
+    HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT:      'HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT',
+    HCI_EXTENDED_INQUIRY_RESULT_EVENT:                     'HCI_EXTENDED_INQUIRY_RESULT_EVENT',
+    HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT:             'HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT',
+    HCI_IO_CAPABILITY_REQUEST_EVENT:                       'HCI_IO_CAPABILITY_REQUEST_EVENT',
+    HCI_IO_CAPABILITY_RESPONSE_EVENT:                      'HCI_IO_CAPABILITY_RESPONSE_EVENT',
+    HCI_USER_CONFIRMATION_REQUEST_EVENT:                   'HCI_USER_CONFIRMATION_REQUEST_EVENT',
+    HCI_USER_PASSKEY_REQUEST_EVENT:                        'HCI_USER_PASSKEY_REQUEST_EVENT',
+    HCI_REMOTE_OOB_DATA_REQUEST:                           'HCI_REMOTE_OOB_DATA_REQUEST',
+    HCI_SIMPLE_PAIRING_COMPLETE_EVENT:                     'HCI_SIMPLE_PAIRING_COMPLETE_EVENT',
+    HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT:            'HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT',
+    HCI_ENHANCED_FLUSH_COMPLETE_EVENT:                     'HCI_ENHANCED_FLUSH_COMPLETE_EVENT',
+    HCI_USER_PASSKEY_NOTIFICATION_EVENT:                   'HCI_USER_PASSKEY_NOTIFICATION_EVENT',
+    HCI_KEYPRESS_NOTIFICATION_EVENT:                       'HCI_KEYPRESS_NOTIFICATION_EVENT',
+    HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT: 'HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT',
+    HCI_LE_META_EVENT:                                     'HCI_LE_META_EVENT'
+}
+
+# HCI Subevent Codes
+HCI_LE_CONNECTION_COMPLETE_EVENT                   = 0x01
+HCI_LE_ADVERTISING_REPORT_EVENT                    = 0x02
+HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT            = 0x03
+HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT         = 0x04
+HCI_LE_LONG_TERM_KEY_REQUEST_EVENT                 = 0x05
+HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT   = 0x06
+HCI_LE_DATA_LENGTH_CHANGE_EVENT                    = 0x07
+HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT  = 0x08
+HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT               = 0x09
+HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT          = 0x0A
+HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT           = 0x0B
+HCI_LE_PHY_UPDATE_COMPLETE_EVENT                   = 0x0C
+HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT           = 0x0D
+HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT = 0x0E
+HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT           = 0x0F
+HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT        = 0x10
+HCI_LE_SCAN_TIMEOUT_EVENT                          = 0x11
+HCI_LE_ADVERTISING_SET_TERMINATED_EVENT            = 0x12
+HCI_LE_SCAN_REQUEST_RECEIVED_EVENT                 = 0x13
+HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT           = 0x14
+
+HCI_SUBEVENT_NAMES = {
+    HCI_LE_CONNECTION_COMPLETE_EVENT:                   'HCI_LE_CONNECTION_COMPLETE_EVENT',
+    HCI_LE_ADVERTISING_REPORT_EVENT:                    'HCI_LE_ADVERTISING_REPORT_EVENT',
+    HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT:            'HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT',
+    HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT:         'HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT',
+    HCI_LE_LONG_TERM_KEY_REQUEST_EVENT:                 'HCI_LE_LONG_TERM_KEY_REQUEST_EVENT',
+    HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT:   'HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT',
+    HCI_LE_DATA_LENGTH_CHANGE_EVENT:                    'HCI_LE_DATA_LENGTH_CHANGE_EVENT',
+    HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT:  'HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT',
+    HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT:               'HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT',
+    HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT:          'HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT',
+    HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT:           'HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT',
+    HCI_LE_PHY_UPDATE_COMPLETE_EVENT:                   'HCI_LE_PHY_UPDATE_COMPLETE_EVENT',
+    HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT:           'HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT',
+    HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT: 'HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT',
+    HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT:           'HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT',
+    HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT:        'HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT',
+    HCI_LE_SCAN_TIMEOUT_EVENT:                          'HCI_LE_SCAN_TIMEOUT_EVENT',
+    HCI_LE_ADVERTISING_SET_TERMINATED_EVENT:            'HCI_LE_ADVERTISING_SET_TERMINATED_EVENT',
+    HCI_LE_SCAN_REQUEST_RECEIVED_EVENT:                 'HCI_LE_SCAN_REQUEST_RECEIVED_EVENT',
+    HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT:           'HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT'
+}
+
+# HCI Command
+HCI_INQUIRY_COMMAND                                               = hci_command_op_code(0x01, 0x0001)
+HCI_INQUIRY_CANCEL_COMMAND                                        = hci_command_op_code(0x01, 0x0002)
+HCI_CREATE_CONNECTION_COMMAND                                     = hci_command_op_code(0x01, 0x0005)
+HCI_DISCONNECT_COMMAND                                            = hci_command_op_code(0x01, 0x0006)
+HCI_ACCEPT_CONNECTION_REQUEST_COMMAND                             = hci_command_op_code(0x01, 0x0009)
+HCI_LINK_KEY_REQUEST_REPLY_COMMAND                                = hci_command_op_code(0x01, 0x000B)
+HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND                       = hci_command_op_code(0x01, 0x000C)
+HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND                       = hci_command_op_code(0x01, 0x000E)
+HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND                         = hci_command_op_code(0x01, 0x000F)
+HCI_AUTHENTICATION_REQUESTED_COMMAND                              = hci_command_op_code(0x01, 0x0011)
+HCI_SET_CONNECTION_ENCRYPTION_COMMAND                             = hci_command_op_code(0x01, 0x0013)
+HCI_REMOTE_NAME_REQUEST_COMMAND                                   = hci_command_op_code(0x01, 0x0019)
+HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND                        = hci_command_op_code(0x01, 0x001B)
+HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND                         = hci_command_op_code(0x01, 0x001C)
+HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND                       = hci_command_op_code(0x01, 0x001D)
+HCI_READ_CLOCK_OFFSET_COMMAND                                     = hci_command_op_code(0x01, 0x001F)
+HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND                           = hci_command_op_code(0x01, 0x002B)
+HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND                       = hci_command_op_code(0x01, 0x002C)
+HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND              = hci_command_op_code(0x01, 0x002D)
+HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND                            = hci_command_op_code(0x01, 0x002E)
+HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND                   = hci_command_op_code(0x01, 0x002F)
+HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND                 = hci_command_op_code(0x01, 0x003D)
+HCI_SNIFF_MODE_COMMAND                                            = hci_command_op_code(0x02, 0x0003)
+HCI_EXIT_SNIFF_MODE_COMMAND                                       = hci_command_op_code(0x02, 0x0004)
+HCI_SWITCH_ROLE_COMMAND                                           = hci_command_op_code(0x02, 0x000B)
+HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND                            = hci_command_op_code(0x02, 0x000D)
+HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND                    = hci_command_op_code(0x02, 0x000F)
+HCI_SNIFF_SUBRATING_COMMAND                                       = hci_command_op_code(0x02, 0x0011)
+HCI_SET_EVENT_MASK_COMMAND                                        = hci_command_op_code(0x03, 0x0001)
+HCI_RESET_COMMAND                                                 = hci_command_op_code(0x03, 0x0003)
+HCI_SET_EVENT_FILTER_COMMAND                                      = hci_command_op_code(0x03, 0x0005)
+HCI_READ_STORED_LINK_KEY_COMMAND                                  = hci_command_op_code(0x03, 0x000D)
+HCI_DELETE_STORED_LINK_KEY_COMMAND                                = hci_command_op_code(0x03, 0x0012)
+HCI_WRITE_LOCAL_NAME_COMMAND                                      = hci_command_op_code(0x03, 0x0013)
+HCI_READ_LOCAL_NAME_COMMAND                                       = hci_command_op_code(0x03, 0x0014)
+HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND                       = hci_command_op_code(0x03, 0x0016)
+HCI_WRITE_PAGE_TIMEOUT_COMMAND                                    = hci_command_op_code(0x03, 0x0018)
+HCI_WRITE_SCAN_ENABLE_COMMAND                                     = hci_command_op_code(0x03, 0x001A)
+HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND                               = hci_command_op_code(0x03, 0x001B)
+HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND                              = hci_command_op_code(0x03, 0x001C)
+HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND                           = hci_command_op_code(0x03, 0x001E)
+HCI_READ_CLASS_OF_DEVICE_COMMAND                                  = hci_command_op_code(0x03, 0x0023)
+HCI_WRITE_CLASS_OF_DEVICE_COMMAND                                 = hci_command_op_code(0x03, 0x0024)
+HCI_READ_VOICE_SETTING_COMMAND                                    = hci_command_op_code(0x03, 0x0025)
+HCI_WRITE_VOICE_SETTING_COMMAND                                   = hci_command_op_code(0x03, 0x0026)
+HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND                  = hci_command_op_code(0x03, 0x002E)
+HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND                 = hci_command_op_code(0x03, 0x002F)
+HCI_HOST_BUFFER_SIZE_COMMAND                                      = hci_command_op_code(0x03, 0x0033)
+HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND                        = hci_command_op_code(0x03, 0x0037)
+HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND                          = hci_command_op_code(0x03, 0x0038)
+HCI_READ_CURRENT_IAC_LAP_COMMAND                                  = hci_command_op_code(0x03, 0x0039)
+HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND                               = hci_command_op_code(0x03, 0x0043)
+HCI_WRITE_INQUIRY_MODE_COMMAND                                    = hci_command_op_code(0x03, 0x0045)
+HCI_READ_PAGE_SCAN_TYPE_COMMAND                                   = hci_command_op_code(0x03, 0x0046)
+HCI_WRITE_PAGE_SCAN_TYPE_COMMAND                                  = hci_command_op_code(0x03, 0x0047)
+HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND                       = hci_command_op_code(0x03, 0x0052)
+HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND                             = hci_command_op_code(0x03, 0x0056)
+HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND            = hci_command_op_code(0x03, 0x0058)
+HCI_SET_EVENT_MASK_PAGE_2_COMMAND                                 = hci_command_op_code(0x03, 0x0063)
+HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND                 = hci_command_op_code(0x03, 0x005A)
+HCI_READ_LE_HOST_SUPPORT_COMMAND                                  = hci_command_op_code(0x03, 0x006C)
+HCI_WRITE_LE_HOST_SUPPORT_COMMAND                                 = hci_command_op_code(0x03, 0x006D)
+HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND                 = hci_command_op_code(0x03, 0x007A)
+HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND                   = hci_command_op_code(0x03, 0x007C)
+HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND                        = hci_command_op_code(0x04, 0x0001)
+HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND                         = hci_command_op_code(0x04, 0x0002)
+HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND                         = hci_command_op_code(0x04, 0x0003)
+HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND                          = hci_command_op_code(0x04, 0x0004)
+HCI_READ_BUFFER_SIZE_COMMAND                                      = hci_command_op_code(0x04, 0x0005)
+HCI_READ_BD_ADDR_COMMAND                                          = hci_command_op_code(0x04, 0x0009)
+HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND                           = hci_command_op_code(0x04, 0x000B)
+HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND                              = hci_command_op_code(0x05, 0x0008)
+HCI_LE_SET_EVENT_MASK_COMMAND                                     = hci_command_op_code(0x08, 0x0001)
+HCI_LE_READ_BUFFER_SIZE_COMMAND                                   = hci_command_op_code(0x08, 0x0002)
+HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND                      = hci_command_op_code(0x08, 0x0003)
+HCI_LE_SET_RANDOM_ADDRESS_COMMAND                                 = hci_command_op_code(0x08, 0x0005)
+HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND                         = hci_command_op_code(0x08, 0x0006)
+HCI_LE_READ_ADVERTISING_CHANNEL_TX_POWER_COMMAND                  = hci_command_op_code(0x08, 0x0007)
+HCI_LE_SET_ADVERTISING_DATA_COMMAND                               = hci_command_op_code(0x08, 0x0008)
+HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND                             = hci_command_op_code(0x08, 0x0009)
+HCI_LE_SET_ADVERTISING_ENABLE_COMMAND                             = hci_command_op_code(0x08, 0x000A)
+HCI_LE_SET_SCAN_PARAMETERS_COMMAND                                = hci_command_op_code(0x08, 0x000B)
+HCI_LE_SET_SCAN_ENABLE_COMMAND                                    = hci_command_op_code(0x08, 0x000C)
+HCI_LE_CREATE_CONNECTION_COMMAND                                  = hci_command_op_code(0x08, 0x000D)
+HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND                           = hci_command_op_code(0x08, 0x000E)
+HCI_LE_READ_WHITE_LIST_SIZE_COMMAND                               = hci_command_op_code(0x08, 0x000F)
+HCI_LE_CLEAR_WHITE_LIST_COMMAND                                   = hci_command_op_code(0x08, 0x0010)
+HCI_LE_ADD_DEVICE_TO_WHITE_LIST_COMMAND                           = hci_command_op_code(0x08, 0x0011)
+HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST_COMMAND                      = hci_command_op_code(0x08, 0x0012)
+HCI_LE_CONNECTION_UPDATE_COMMAND                                  = hci_command_op_code(0x08, 0x0013)
+HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND                    = hci_command_op_code(0x08, 0x0014)
+HCI_LE_READ_CHANNEL_MAP_COMMAND                                   = hci_command_op_code(0x08, 0x0015)
+HCI_LE_READ_REMOTE_FEATURES_COMMAND                               = hci_command_op_code(0x08, 0x0016)
+HCI_LE_ENCRYPT_COMMAND                                            = hci_command_op_code(0x08, 0x0017)
+HCI_LE_RAND_COMMAND                                               = hci_command_op_code(0x08, 0x0018)
+HCI_LE_START_ENCRYPTION_COMMAND                                   = hci_command_op_code(0x08, 0x0019)
+HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND                        = hci_command_op_code(0x08, 0x001A)
+HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND               = hci_command_op_code(0x08, 0x001B)
+HCI_LE_READ_SUPPORTED_STATES_COMMAND                              = hci_command_op_code(0x08, 0x001C)
+HCI_LE_RECEIVER_TEST_COMMAND                                      = hci_command_op_code(0x08, 0x001D)
+HCI_LE_TRANSMITTER_TEST_COMMAND                                   = hci_command_op_code(0x08, 0x001E)
+HCI_LE_TEST_END_COMMAND                                           = hci_command_op_code(0x08, 0x001F)
+HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND          = hci_command_op_code(0x08, 0x0020)
+HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x0021)
+HCI_LE_SET_DATA_LENGTH_COMMAND                                    = hci_command_op_code(0x08, 0x0022)
+HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND                 = hci_command_op_code(0x08, 0x0023)
+HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND                = hci_command_op_code(0x08, 0x0024)
+HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND                        = hci_command_op_code(0x08, 0x0025)
+HCI_LE_GENERATE_DHKEY_COMMAND                                     = hci_command_op_code(0x08, 0x0026)
+HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND                       = hci_command_op_code(0x08, 0x0027)
+HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND                  = hci_command_op_code(0x08, 0x0028)
+HCI_LE_CLEAR_RESOLVING_LIST_COMMAND                               = hci_command_op_code(0x08, 0x0029)
+HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND                           = hci_command_op_code(0x08, 0x002A)
+HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND                       = hci_command_op_code(0x08, 0x002B)
+HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND                      = hci_command_op_code(0x08, 0x002C)
+HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND                      = hci_command_op_code(0x08, 0x002D)
+HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND             = hci_command_op_code(0x08, 0x002E)
+HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND                           = hci_command_op_code(0x08, 0x002F)
+HCI_LE_READ_PHY_COMMAND                                           = hci_command_op_code(0x08, 0x0030)
+HCI_LE_SET_DEFAULT_PHY_COMMAND                                    = hci_command_op_code(0x08, 0x0031)
+HCI_LE_SET_PHY_COMMAND                                            = hci_command_op_code(0x08, 0x0032)
+HCI_LE_ENHANCED_RECEIVER_TEST_COMMAND                             = hci_command_op_code(0x08, 0x0033)
+HCI_LE_ENHANCED_TRANSMITTER_TEST_COMMAND                          = hci_command_op_code(0x08, 0x0034)
+HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND                 = hci_command_op_code(0x08, 0x0035)
+HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND                = hci_command_op_code(0x08, 0x0036)
+HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND                      = hci_command_op_code(0x08, 0x0037)
+HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND                    = hci_command_op_code(0x08, 0x0038)
+HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND                    = hci_command_op_code(0x08, 0x0039)
+HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND               = hci_command_op_code(0x08, 0x003A)
+HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERETISING_SETS_COMMAND         = hci_command_op_code(0x08, 0x003B)
+HCI_LE_REMOVE_ADVERTISING_SET_COMMAND                             = hci_command_op_code(0x08, 0x003C)
+HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND                             = hci_command_op_code(0x08, 0x003D)
+HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND                = hci_command_op_code(0x08, 0x003E)
+HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND                      = hci_command_op_code(0x08, 0x003F)
+HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND                    = hci_command_op_code(0x08, 0x0040)
+HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND                       = hci_command_op_code(0x08, 0x0041)
+HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND                           = hci_command_op_code(0x08, 0x0042)
+HCI_LE_SET_EXTENDED_CREATE_CONNECTION_COMMAND                     = hci_command_op_code(0x08, 0x0043)
+HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND                   = hci_command_op_code(0x08, 0x0044)
+HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND            = hci_command_op_code(0x08, 0x0045)
+HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND                = hci_command_op_code(0x08, 0x0046)
+HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND             = hci_command_op_code(0x08, 0x0047)
+HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND        = hci_command_op_code(0x08, 0x0048)
+HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND                     = hci_command_op_code(0x08, 0x0049)
+HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND                 = hci_command_op_code(0x08, 0x004A)
+HCI_LE_READ_TRANSMIT_POWER_COMMAND                                = hci_command_op_code(0x08, 0x004B)
+HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND                          = hci_command_op_code(0x08, 0x004C)
+HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND                         = hci_command_op_code(0x08, 0x004D)
+HCI_LE_SET_PRIVACY_MODE_COMMAND                                   = hci_command_op_code(0x08, 0x004E)
+
+
+HCI_COMMAND_NAMES = {
+    HCI_INQUIRY_COMMAND:                                               'HCI_INQUIRY_COMMAND',
+    HCI_INQUIRY_CANCEL_COMMAND:                                        'HCI_INQUIRY_CANCEL_COMMAND',
+    HCI_CREATE_CONNECTION_COMMAND:                                     'HCI_CREATE_CONNECTION_COMMAND',
+    HCI_DISCONNECT_COMMAND:                                            'HCI_DISCONNECT_COMMAND',
+    HCI_ACCEPT_CONNECTION_REQUEST_COMMAND:                             'HCI_ACCEPT_CONNECTION_REQUEST_COMMAND',
+    HCI_LINK_KEY_REQUEST_REPLY_COMMAND:                                'HCI_LINK_KEY_REQUEST_REPLY_COMMAND',
+    HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND:                       'HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND',
+    HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND:                       'HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND',
+    HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND:                         'HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND',
+    HCI_AUTHENTICATION_REQUESTED_COMMAND:                              'HCI_AUTHENTICATION_REQUESTED_COMMAND',
+    HCI_SET_CONNECTION_ENCRYPTION_COMMAND:                             'HCI_SET_CONNECTION_ENCRYPTION_COMMAND',
+    HCI_REMOTE_NAME_REQUEST_COMMAND:                                   'HCI_REMOTE_NAME_REQUEST_COMMAND',
+    HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND:                        'HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND',
+    HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND:                         'HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND',
+    HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND:                       'HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND',
+    HCI_READ_CLOCK_OFFSET_COMMAND:                                     'HCI_READ_CLOCK_OFFSET_COMMAND',
+    HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND:                           'HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND',
+    HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND:                       'HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND',
+    HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND:              'HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND',
+    HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND:                            'HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND',
+    HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND:                   'HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND',
+    HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND:                 'HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND',
+    HCI_SNIFF_MODE_COMMAND:                                            'HCI_SNIFF_MODE_COMMAND',
+    HCI_EXIT_SNIFF_MODE_COMMAND:                                       'HCI_EXIT_SNIFF_MODE_COMMAND',
+    HCI_SWITCH_ROLE_COMMAND:                                           'HCI_SWITCH_ROLE_COMMAND',
+    HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND:                            'HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND',
+    HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND:                    'HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND',
+    HCI_SNIFF_SUBRATING_COMMAND:                                       'HCI_SNIFF_SUBRATING_COMMAND',
+    HCI_SET_EVENT_MASK_COMMAND:                                        'HCI_SET_EVENT_MASK_COMMAND',
+    HCI_RESET_COMMAND:                                                 'HCI_RESET_COMMAND',
+    HCI_SET_EVENT_FILTER_COMMAND:                                      'HCI_SET_EVENT_FILTER_COMMAND',
+    HCI_READ_STORED_LINK_KEY_COMMAND:                                  'HCI_READ_STORED_LINK_KEY_COMMAND',
+    HCI_DELETE_STORED_LINK_KEY_COMMAND:                                'HCI_DELETE_STORED_LINK_KEY_COMMAND',
+    HCI_WRITE_LOCAL_NAME_COMMAND:                                      'HCI_WRITE_LOCAL_NAME_COMMAND',
+    HCI_READ_LOCAL_NAME_COMMAND:                                       'HCI_READ_LOCAL_NAME_COMMAND',
+    HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND:                       'HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND',
+    HCI_WRITE_PAGE_TIMEOUT_COMMAND:                                    'HCI_WRITE_PAGE_TIMEOUT_COMMAND',
+    HCI_WRITE_SCAN_ENABLE_COMMAND:                                     'HCI_WRITE_SCAN_ENABLE_COMMAND',
+    HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND:                               'HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND',
+    HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND:                              'HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND',
+    HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND:                           'HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND',
+    HCI_READ_CLASS_OF_DEVICE_COMMAND:                                  'HCI_READ_CLASS_OF_DEVICE_COMMAND',
+    HCI_WRITE_CLASS_OF_DEVICE_COMMAND:                                 'HCI_WRITE_CLASS_OF_DEVICE_COMMAND',
+    HCI_READ_VOICE_SETTING_COMMAND:                                    'HCI_READ_VOICE_SETTING_COMMAND',
+    HCI_WRITE_VOICE_SETTING_COMMAND:                                   'HCI_WRITE_VOICE_SETTING_COMMAND',
+    HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND:                  'HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND',
+    HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND:                 'HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND',
+    HCI_HOST_BUFFER_SIZE_COMMAND:                                      'HCI_HOST_BUFFER_SIZE_COMMAND',
+    HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND:                        'HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND',
+    HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND:                          'HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND',
+    HCI_READ_CURRENT_IAC_LAP_COMMAND:                                  'HCI_READ_CURRENT_IAC_LAP_COMMAND',
+    HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND:                               'HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND',
+    HCI_WRITE_INQUIRY_MODE_COMMAND:                                    'HCI_WRITE_INQUIRY_MODE_COMMAND',
+    HCI_READ_PAGE_SCAN_TYPE_COMMAND:                                   'HCI_READ_PAGE_SCAN_TYPE_COMMAND',
+    HCI_WRITE_PAGE_SCAN_TYPE_COMMAND:                                  'HCI_WRITE_PAGE_SCAN_TYPE_COMMAND',
+    HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND:                       'HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND',
+    HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND:                             'HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND',
+    HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND:            'HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND',
+    HCI_SET_EVENT_MASK_PAGE_2_COMMAND:                                 'HCI_SET_EVENT_MASK_PAGE_2_COMMAND',
+    HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND:                 'HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND',
+    HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND:                        'HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND',
+    HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND:                         'HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND',
+    HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND:                         'HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND',
+    HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND:                          'HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND',
+    HCI_READ_BUFFER_SIZE_COMMAND:                                      'HCI_READ_BUFFER_SIZE_COMMAND',
+    HCI_READ_LE_HOST_SUPPORT_COMMAND:                                  'HCI_READ_LE_HOST_SUPPORT_COMMAND',
+    HCI_WRITE_LE_HOST_SUPPORT_COMMAND:                                 'HCI_WRITE_LE_HOST_SUPPORT_COMMAND',
+    HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND:                 'HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND',
+    HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND:                   'HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND',
+    HCI_READ_BD_ADDR_COMMAND:                                          'HCI_READ_BD_ADDR_COMMAND',
+    HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND:                           'HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND',
+    HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND:                              'HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND',
+    HCI_LE_SET_EVENT_MASK_COMMAND:                                     'HCI_LE_SET_EVENT_MASK_COMMAND',
+    HCI_LE_READ_BUFFER_SIZE_COMMAND:                                   'HCI_LE_READ_BUFFER_SIZE_COMMAND',
+    HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND:                      'HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND',
+    HCI_LE_SET_RANDOM_ADDRESS_COMMAND:                                 'HCI_LE_SET_RANDOM_ADDRESS_COMMAND',
+    HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND:                         'HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND',
+    HCI_LE_READ_ADVERTISING_CHANNEL_TX_POWER_COMMAND:                  'HCI_LE_READ_ADVERTISING_CHANNEL_TX_POWER_COMMAND',
+    HCI_LE_SET_ADVERTISING_DATA_COMMAND:                               'HCI_LE_SET_ADVERTISING_DATA_COMMAND',
+    HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND:                             'HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND',
+    HCI_LE_SET_ADVERTISING_ENABLE_COMMAND:                             'HCI_LE_SET_ADVERTISING_ENABLE_COMMAND',
+    HCI_LE_SET_SCAN_PARAMETERS_COMMAND:                                'HCI_LE_SET_SCAN_PARAMETERS_COMMAND',
+    HCI_LE_SET_SCAN_ENABLE_COMMAND:                                    'HCI_LE_SET_SCAN_ENABLE_COMMAND',
+    HCI_LE_CREATE_CONNECTION_COMMAND:                                  'HCI_LE_CREATE_CONNECTION_COMMAND',
+    HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND:                           'HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND',
+    HCI_LE_READ_WHITE_LIST_SIZE_COMMAND:                               'HCI_LE_READ_WHITE_LIST_SIZE_COMMAND',
+    HCI_LE_CLEAR_WHITE_LIST_COMMAND:                                   'HCI_LE_CLEAR_WHITE_LIST_COMMAND',
+    HCI_LE_ADD_DEVICE_TO_WHITE_LIST_COMMAND:                           'HCI_LE_ADD_DEVICE_TO_WHITE_LIST_COMMAND',
+    HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST_COMMAND:                      'HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST_COMMAND',
+    HCI_LE_CONNECTION_UPDATE_COMMAND:                                  'HCI_LE_CONNECTION_UPDATE_COMMAND',
+    HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND:                    'HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND',
+    HCI_LE_READ_CHANNEL_MAP_COMMAND:                                   'HCI_LE_READ_CHANNEL_MAP_COMMAND',
+    HCI_LE_READ_REMOTE_FEATURES_COMMAND:                               'HCI_LE_READ_REMOTE_FEATURES_COMMAND',
+    HCI_LE_ENCRYPT_COMMAND:                                            'HCI_LE_ENCRYPT_COMMAND',
+    HCI_LE_RAND_COMMAND:                                               'HCI_LE_RAND_COMMAND',
+    HCI_LE_START_ENCRYPTION_COMMAND:                                   'HCI_LE_START_ENCRYPTION_COMMAND',
+    HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND:                        'HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND',
+    HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND:               'HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND',
+    HCI_LE_READ_SUPPORTED_STATES_COMMAND:                              'HCI_LE_READ_SUPPORTED_STATES_COMMAND',
+    HCI_LE_RECEIVER_TEST_COMMAND:                                      'HCI_LE_RECEIVER_TEST_COMMAND',
+    HCI_LE_TRANSMITTER_TEST_COMMAND:                                   'HCI_LE_TRANSMITTER_TEST_COMMAND',
+    HCI_LE_TEST_END_COMMAND:                                           'HCI_LE_TEST_END_COMMAND',
+    HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND:          'HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND',
+    HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND',
+    HCI_LE_SET_DATA_LENGTH_COMMAND:                                    'HCI_LE_SET_DATA_LENGTH_COMMAND',
+    HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND:                 'HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND',
+    HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND:                'HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND',
+    HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND:                        'HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND',
+    HCI_LE_GENERATE_DHKEY_COMMAND:                                     'HCI_LE_GENERATE_DHKEY_COMMAND',
+    HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND:                       'HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND',
+    HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND:                  'HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND',
+    HCI_LE_CLEAR_RESOLVING_LIST_COMMAND:                               'HCI_LE_CLEAR_RESOLVING_LIST_COMMAND',
+    HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND:                           'HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND',
+    HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND:                       'HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND',
+    HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND:                      'HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND',
+    HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND:                      'HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND',
+    HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND:             'HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND',
+    HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND:                           'HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND',
+    HCI_LE_READ_PHY_COMMAND:                                           'HCI_LE_READ_PHY_COMMAND',
+    HCI_LE_SET_DEFAULT_PHY_COMMAND:                                    'HCI_LE_SET_DEFAULT_PHY_COMMAND',
+    HCI_LE_SET_PHY_COMMAND:                                            'HCI_LE_SET_PHY_COMMAND',
+    HCI_LE_ENHANCED_RECEIVER_TEST_COMMAND:                             'HCI_LE_ENHANCED_RECEIVER_TEST_COMMAND',
+    HCI_LE_ENHANCED_TRANSMITTER_TEST_COMMAND:                          'HCI_LE_ENHANCED_TRANSMITTER_TEST_COMMAND',
+    HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND:                 'HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND',
+    HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND:                'HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND',
+    HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND:                      'HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND',
+    HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND:                    'HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND',
+    HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND:                    'HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND',
+    HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND:               'HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND',
+    HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERETISING_SETS_COMMAND:         'HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERETISING_SETS_COMMAND',
+    HCI_LE_REMOVE_ADVERTISING_SET_COMMAND:                             'HCI_LE_REMOVE_ADVERTISING_SET_COMMAND',
+    HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND:                             'HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND',
+    HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND:                'HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND',
+    HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND:                      'HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND',
+    HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND:                    'HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND',
+    HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND:                       'HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND',
+    HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND:                           'HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND',
+    HCI_LE_SET_EXTENDED_CREATE_CONNECTION_COMMAND:                     'HCI_LE_SET_EXTENDED_CREATE_CONNECTION_COMMAND',
+    HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND:                   'HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND',
+    HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND:            'HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND',
+    HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND:                'HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND',
+    HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND:             'HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND',
+    HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND:        'HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND',
+    HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND:                     'HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND',
+    HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND:                 'HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND',
+    HCI_LE_READ_TRANSMIT_POWER_COMMAND:                                'HCI_LE_READ_TRANSMIT_POWER_COMMAND',
+    HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND:                          'HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND',
+    HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND:                         'HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND',
+    HCI_LE_SET_PRIVACY_MODE_COMMAND:                                   'HCI_LE_SET_PRIVACY_MODE_COMMAND'
+}
+
+
+# HCI Error Codes
+# See Bluetooth spec Vol 2, Part D - 1.3 LIST OF ERROR CODES
+HCI_SUCCESS                                                        = 0x00
+HCI_UNKNOWN_HCI_COMMAND_ERROR                                      = 0x01
+HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR                            = 0x02
+HCI_HARDWARE_FAILURE_ERROR                                         = 0x03
+HCI_PAGE_TIMEOUT_ERROR                                             = 0x04
+HCI_AUTHENTICATION_FAILURE_ERROR                                   = 0x05
+HCI_PIN_OR_KEY_MISSING_ERROR                                       = 0x06
+HCI_MEMORY_CAPACITY_EXCEEDED_ERROR                                 = 0x07
+HCI_CONNECTION_TIMEOUT_ERROR                                       = 0x08
+HCI_CONNECTION_LIMIT_EXCEEDED_ERROR                                = 0x09
+HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR        = 0x0A
+HCI_CONNECTION_ALREADY_EXISTS_ERROR                                = 0x0B
+HCI_COMMAND_DISALLOWED_ERROR                                       = 0x0C
+HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR             = 0x0D
+HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR              = 0x0E
+HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR          = 0x0F
+HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR                                = 0x10
+HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR                   = 0x11
+HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR                           = 0x12
+HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR                        = 0x13
+HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR = 0x14
+HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR     = 0x15
+HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR                      = 0x16
+HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR                       = 0x3B
+HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR                      = 0x3E
+# TODO: more error codes
+
+HCI_ERROR_NAMES = {
+    HCI_SUCCESS:                                                        'HCI_SUCCESS',
+    HCI_UNKNOWN_HCI_COMMAND_ERROR:                                      'HCI_UNKNOWN_HCI_COMMAND_ERROR',
+    HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR:                            'HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR',
+    HCI_HARDWARE_FAILURE_ERROR:                                         'HCI_HARDWARE_FAILURE_ERROR',
+    HCI_PAGE_TIMEOUT_ERROR:                                             'HCI_PAGE_TIMEOUT_ERROR',
+    HCI_AUTHENTICATION_FAILURE_ERROR:                                   'HCI_AUTHENTICATION_FAILURE_ERROR',
+    HCI_PIN_OR_KEY_MISSING_ERROR:                                       'HCI_PIN_OR_KEY_MISSING_ERROR',
+    HCI_MEMORY_CAPACITY_EXCEEDED_ERROR:                                 'HCI_MEMORY_CAPACITY_EXCEEDED_ERROR',
+    HCI_CONNECTION_TIMEOUT_ERROR:                                       'HCI_CONNECTION_TIMEOUT_ERROR',
+    HCI_CONNECTION_LIMIT_EXCEEDED_ERROR:                                'HCI_CONNECTION_LIMIT_EXCEEDED_ERROR',
+    HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR:        'HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR',
+    HCI_CONNECTION_ALREADY_EXISTS_ERROR:                                'HCI_CONNECTION_ALREADY_EXISTS_ERROR',
+    HCI_COMMAND_DISALLOWED_ERROR:                                       'HCI_COMMAND_DISALLOWED_ERROR',
+    HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR:             'HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR',
+    HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR:              'HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR',
+    HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR:          'HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR',
+    HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR:                                'HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR',
+    HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR:                   'HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR',
+    HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR:                           'HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR',
+    HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR:                        'HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR',
+    HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR: 'HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR',
+    HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR:     'HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR',
+    HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR:                      'HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR',
+    HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR:                       'HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR',
+    HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR:                      'HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR'
+}
+
+# Command Status codes
+HCI_COMMAND_STATUS_PENDING = 0
+
+# LE Event Masks
+LE_CONNECTION_COMPLETE_EVENT_MASK                   = (1 << 0)
+LE_ADVERTISING_REPORT_EVENT_MASK                    = (1 << 1)
+LE_CONNECTION_UPDATE_COMPLETE_EVENT_MASK            = (1 << 2)
+LE_READ_REMOTE_FEATURES_COMPLETE_EVENT_MASK         = (1 << 3)
+LE_LONG_TERM_KEY_REQUEST_EVENT_MASK                 = (1 << 4)
+LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT_MASK   = (1 << 5)
+LE_DATA_LENGTH_CHANGE_EVENT_MASK                    = (1 << 6)
+LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT_MASK  = (1 << 7)
+LE_GENERATE_DHKEY_COMPLETE_EVENT_MASK               = (1 << 8)
+LE_ENHANCED_CONNECTION_COMPLETE_EVENT_MASK          = (1 << 9)
+LE_DIRECTED_ADVERTISING_REPORT_EVENT_MASK           = (1 << 10)
+LE_PHY_UPDATE_COMPLETE_EVENT_MASK                   = (1 << 11)
+LE_EXTENDED_ADVERTISING_REPORT_EVENT_MASK           = (1 << 12)
+LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT_MASK = (1 << 13)
+LE_PERIODIC_ADVERTISING_REPORT_EVENT_MASK           = (1 << 14)
+LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT_MASK        = (1 << 15)
+LE_EXTENDED_SCAN_TIMEOUT_EVENT_MASK                 = (1 << 16)
+LE_EXTENDED_ADVERTISING_SET_TERMINATED_EVENT_MASK   = (1 << 17)
+LE_SCAN_REQUEST_RECEIVED_EVENT_MASK                 = (1 << 18)
+LE_CHANNEL_SELECTION_ALGORITHM_EVENT_MASK           = (1 << 19)
+
+# ACL
+HCI_ACL_PB_FIRST_NON_FLUSHABLE = 0
+HCI_ACL_PB_CONTINUATION        = 1
+HCI_ACL_PB_FIRST_FLUSHABLE     = 2
+HCI_ACK_PB_COMPLETE_L2CAP      = 3
+
+# Roles
+HCI_CENTRAL_ROLE    = 0
+HCI_PERIPHERAL_ROLE = 1
+
+HCI_ROLE_NAMES = {
+    HCI_CENTRAL_ROLE:    'CENTRAL',
+    HCI_PERIPHERAL_ROLE: 'PERIPHERAL'
+}
+
+# LE PHY Types
+HCI_LE_1M_PHY    = 1
+HCI_LE_2M_PHY    = 2
+HCI_LE_CODED_PHY = 3
+
+HCI_LE_PHY_NAMES = {
+    HCI_LE_1M_PHY:    'LE 1M',
+    HCI_LE_2M_PHY:    'L2 2M',
+    HCI_LE_CODED_PHY: 'LE Coded'
+}
+
+# Connection Parameters
+HCI_CONNECTION_INTERVAL_MS_PER_UNIT = 1.25
+HCI_CONNECTION_LATENCY_MS_PER_UNIT  = 1.25
+HCI_SUPERVISION_TIMEOUT_MS_PER_UNIT = 10
+
+# Inquiry LAP
+HCI_LIMITED_DEDICATED_INQUIRY_LAP = 0x9E8B00
+HCI_GENERAL_INQUIRY_LAP           = 0x9E8B33
+HCI_INQUIRY_LAP_NAMES = {
+    HCI_LIMITED_DEDICATED_INQUIRY_LAP: 'Limited Dedicated Inquiry',
+    HCI_GENERAL_INQUIRY_LAP:           'General Inquiry'
+}
+
+# Inquiry Mode
+HCI_STANDARD_INQUIRY_MODE  = 0x00
+HCI_INQUIRY_WITH_RSSI_MODE = 0x01
+HCI_EXTENDED_INQUIRY_MODE  = 0x02
+
+# Page Scan Repetition Mode
+HCI_R0_PAGE_SCAN_REPETITION_MODE = 0x00
+HCI_R1_PAGE_SCAN_REPETITION_MODE = 0x01
+HCI_R2_PAGE_SCAN_REPETITION_MODE = 0x02
+
+# IO Capability
+HCI_DISPLAY_ONLY_IO_CAPABILITY       = 0x00
+HCI_DISPLAY_YES_NO_IO_CAPABILITY     = 0x01
+HCI_KEYBOARD_ONLY_IO_CAPABILITY      = 0x02
+HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY = 0x03
+
+HCI_IO_CAPABILITY_NAMES = {
+    HCI_DISPLAY_ONLY_IO_CAPABILITY:       'HCI_DISPLAY_ONLY_IO_CAPABILITY',
+    HCI_DISPLAY_YES_NO_IO_CAPABILITY:     'HCI_DISPLAY_YES_NO_IO_CAPABILITY',
+    HCI_KEYBOARD_ONLY_IO_CAPABILITY:      'HCI_KEYBOARD_ONLY_IO_CAPABILITY',
+    HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: 'HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY'
+}
+
+# Authentication Requirements
+HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS        = 0x00
+HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS            = 0x01
+HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS = 0x02
+HCI_MITM_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS     = 0x03
+HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS   = 0x04
+HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS       = 0x05
+
+HCI_AUTHENTICATION_REQUIREMENTS_NAMES = {
+    HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS:        'HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS',
+    HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS:            'HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS',
+    HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS',
+    HCI_MITM_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS:     'HCI_MITM_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS',
+    HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS:   'HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS',
+    HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS:       'HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS'
+}
+
+# Link Key Types
+HCI_COMBINATION_KEY_TYPE                                      = 0X00
+HCI_LOCAL_UNIT_KEY_TYPE                                       = 0X01
+HCI_REMOTE_UNIT_KEY_TYPE                                      = 0X02
+HCI_DEBUG_COMBINATION_KEY_TYPE                                = 0X03
+HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE = 0X04
+HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE   = 0X05
+HCI_CHANGED_COMBINATION_KEY_TYPE                              = 0X06
+HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE = 0X07
+HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE   = 0X08
+
+HCI_LINK_TYPE_NAMES = {
+    HCI_COMBINATION_KEY_TYPE:                                      'HCI_COMBINATION_KEY_TYPE',
+    HCI_LOCAL_UNIT_KEY_TYPE:                                       'HCI_LOCAL_UNIT_KEY_TYPE',
+    HCI_REMOTE_UNIT_KEY_TYPE:                                      'HCI_REMOTE_UNIT_KEY_TYPE',
+    HCI_DEBUG_COMBINATION_KEY_TYPE:                                'HCI_DEBUG_COMBINATION_KEY_TYPE',
+    HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE: 'HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE',
+    HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE:   'HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE',
+    HCI_CHANGED_COMBINATION_KEY_TYPE:                              'HCI_CHANGED_COMBINATION_KEY_TYPE',
+    HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE: 'HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE',
+    HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE:   'HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE'
+}
+
+# Address types
+HCI_PUBLIC_DEVICE_ADDRESS_TYPE   = 0x00
+HCI_RANDOM_DEVICE_ADDRESS_TYPE   = 0x01
+HCI_PUBLIC_IDENTITY_ADDRESS_TYPE = 0x02
+HCI_RANDOM_IDENTITY_ADDRESS_TYPE = 0x03
+
+# -----------------------------------------------------------------------------
+STATUS_SPEC = {'size': 1, 'mapper': lambda x: HCI_Constant.status_name(x)}
+
+
+# -----------------------------------------------------------------------------
+class HCI_Constant:
+    @staticmethod
+    def status_name(status):
+        return HCI_ERROR_NAMES.get(status, f'0x{status:02X}')
+
+    @staticmethod
+    def error_name(status):
+        return HCI_ERROR_NAMES.get(status, f'0x{status:02X}')
+
+    @staticmethod
+    def role_name(role):
+        return HCI_ROLE_NAMES.get(role, str(role))
+
+    @staticmethod
+    def le_phy_name(phy):
+        return HCI_LE_PHY_NAMES.get(phy, str(phy))
+
+    @staticmethod
+    def inquiry_lap_name(lap):
+        return HCI_INQUIRY_LAP_NAMES.get(lap, f'0x{lap:06X}')
+
+    @staticmethod
+    def io_capability_name(io_capability):
+        return HCI_IO_CAPABILITY_NAMES.get(io_capability, f'0x{io_capability:02X}')
+
+    @staticmethod
+    def authentication_requirements_name(authentication_requirements):
+        return HCI_AUTHENTICATION_REQUIREMENTS_NAMES.get(
+            authentication_requirements,
+            f'0x{authentication_requirements:02X}'
+        )
+
+    @staticmethod
+    def link_key_type_name(link_key_type):
+        return HCI_LINK_TYPE_NAMES.get(link_key_type, f'0x{link_key_type:02X}')
+
+
+# -----------------------------------------------------------------------------
+class HCI_Error(ProtocolError):
+    def __init__(self, error_code):
+        super().__init__(error_code, 'hci', HCI_Constant.error_name(error_code))
+
+
+# -----------------------------------------------------------------------------
+# Generic HCI object
+# -----------------------------------------------------------------------------
+class HCI_Object:
+    @staticmethod
+    def init_from_fields(object, fields, values):
+        if type(values) is dict:
+            for field_name, _ in fields:
+                setattr(object, field_name, values[field_name])
+        else:
+            for field_name, field_value in zip(fields, values):
+                setattr(object, field_name, field_value)
+
+    @staticmethod
+    def init_from_bytes(object, data, offset, fields):
+        parsed = HCI_Object.dict_from_bytes(data, offset, fields)
+        HCI_Object.init_from_fields(object, parsed.keys(), parsed.values())
+
+    @staticmethod
+    def dict_from_bytes(data, offset, fields):
+        result = collections.OrderedDict()
+        for (field_name, field_type) in fields:
+            # The field_type may be a dictionnary with a mapper, parser, and/or size
+            if type(field_type) is dict:
+                if 'size' in field_type:
+                    field_type = field_type['size']
+                elif 'parser' in field_type:
+                    field_type = field_type['parser']
+
+            # Parse the field
+            if field_type == '*':
+                # The rest of the bytes
+                field_value = data[offset:]
+                offset += len(field_value)
+            elif field_type == 1:
+                # 8-bit unsigned
+                field_value = data[offset]
+                offset += 1
+            elif field_type == -1:
+                # 8-bit signed
+                field_value = struct.unpack_from('b', data, offset)[0]
+                offset += 1
+            elif field_type == 2:
+                # 16-bit unsigned
+                field_value = struct.unpack_from('<H', data, offset)[0]
+                offset += 2
+            elif field_type == '>2':
+                # 16-bit unsigned big-endian
+                field_value = struct.unpack_from('>H', data, offset)[0]
+                offset += 2
+            elif field_type == -2:
+                # 16-bit signed
+                field_value = struct.unpack_from('<h', data, offset)[0]
+                offset += 1
+            elif field_type == 3:
+                # 24-bit unsigned
+                padded = data[offset:offset + 3] + bytes([0])
+                field_value = struct.unpack('<I', padded)[0]
+                offset += 3
+            elif field_type == 4:
+                # 32-bit unsigned
+                field_value = struct.unpack_from('<I', data, offset)[0]
+                offset += 4
+            elif field_type == '>4':
+                # 32-bit unsigned big-endian
+                field_value = struct.unpack_from('>I', data, offset)[0]
+                offset += 4
+            elif type(field_type) is int and field_type > 4 and field_type <= 256:
+                # Byte array (from 5 up to 256 bytes)
+                field_value = data[offset:offset + field_type]
+                offset += field_type
+            elif callable(field_type):
+                offset, field_value = field_type(data, offset)
+            else:
+                raise ValueError(f'unknown field type {field_type}')
+
+            result[field_name] = field_value
+
+        return result
+
+    @staticmethod
+    def dict_to_bytes(object, fields):
+        result = bytearray()
+        for (field_name, field_type) in fields:
+            # The field_type may be a dictionnary with a mapper, parser, serializer, and/or size
+            serializer = None
+            if type(field_type) is dict:
+                if 'serializer' in field_type:
+                    serializer = field_type['serializer']
+                if 'size' in field_type:
+                    field_type = field_type['size']
+
+            # Serialize the field
+            field_value = object[field_name]
+            if serializer:
+                field_bytes = serializer(field_value)
+            elif field_type == 1:
+                # 8-bit unsigned
+                field_bytes = bytes([field_value])
+            elif field_type == -1:
+                # 8-bit signed
+                field_bytes = struct.pack('b', field_value)
+            elif field_type == 2:
+                # 16-bit unsigned
+                field_bytes = struct.pack('<H', field_value)
+            elif field_type == '>2':
+                # 16-bit unsigned big-endian
+                field_bytes = struct.pack('>H', field_value)
+            elif field_type == -2:
+                # 16-bit signed
+                field_bytes = struct.pack('<h', field_value)
+            elif field_type == 3:
+                # 24-bit unsigned
+                field_bytes = struct.pack('<I', field_value)[0:3]
+            elif field_type == 4:
+                # 32-bit unsigned
+                field_bytes = struct.pack('<I', field_value)
+            elif field_type == '>4':
+                # 32-bit unsigned big-endian
+                field_bytes = struct.pack('>I', field_value)
+            elif field_type == '*':
+                if type(field_value) is int:
+                    if field_value >= 0 and field_value <= 255:
+                        field_bytes = bytes([field_value])
+                    else:
+                        raise ValueError('value too large for *-typed field')
+                else:
+                    field_bytes = bytes(field_value)
+            elif type(field_value) is bytes or type(field_value) is bytearray or hasattr(field_value, 'to_bytes'):
+                field_bytes = bytes(field_value)
+                if type(field_type) is int and field_type > 4 and field_type <= 256:
+                    # Truncate or Pad with zeros if the field is too long or too short
+                    if len(field_bytes) < field_type:
+                        field_bytes += bytes(field_type - len(field_bytes))
+                    elif len(field_bytes) > field_type:
+                        field_bytes = field_bytes[:field_type]
+            else:
+                raise ValueError(f"don't know how to serialize type {type(field_value)}")
+
+            result += field_bytes
+
+        return bytes(result)
+
+    @staticmethod
+    def from_bytes(data, offset, fields):
+        return HCI_Object(fields, **HCI_Object.dict_from_bytes(data, offset, fields))
+
+    def to_bytes(self):
+        return HCI_Object.dict_to_bytes(self.__dict__, self.fields)
+
+    @staticmethod
+    def parse_length_prefixed_bytes(data, offset):
+        length = data[offset]
+        return offset + 1 + length, data[offset + 1:offset + 1 + length]
+
+    @staticmethod
+    def serialize_length_prefixed_bytes(data, padded_size=0):
+        prefixed_size = 1 + len(data)
+        padding = bytes(padded_size - prefixed_size) if prefixed_size < padded_size else b''
+        return bytes([len(data)]) + data + padding
+
+    @staticmethod
+    def format_field_value(value, indentation):
+        if type(value) is bytes:
+            return value.hex()
+        elif isinstance(value, HCI_Object):
+            return '\n' + value.to_string(indentation)
+        else:
+            return str(value)
+
+    @staticmethod
+    def format_fields(object, keys, indentation='', value_mappers={}):
+        if not keys:
+            return ''
+
+        # Measure the widest field name
+        max_field_name_length = max([len(key[0] if type(key) is tuple else key) for key in keys])
+
+        # Build array of formatted key:value pairs
+        fields = []
+        for key in keys:
+            value_mapper = None
+            if type(key) is tuple:
+                # The key has an associated specifier
+                key, specifier = key
+
+                # Get the value mapper from the specifier
+                if type(specifier) is dict:
+                    value_mapper = specifier.get('mapper')
+
+            # Get the value for the field
+            value = object[key]
+
+            # Map the value if needed
+            value_mapper = value_mappers.get(key, value_mapper)
+            if value_mapper is not None:
+                value = value_mapper(value)
+
+            # Get the string representation of the value
+            value_str = HCI_Object.format_field_value(value, indentation = indentation + '  ')
+
+            # Add the field to the formatted result
+            key_str = color(f'{key + ":":{1 + max_field_name_length}}', 'cyan')
+            fields.append(f'{indentation}{key_str} {value_str}')
+
+        return '\n'.join(fields)
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __init__(self, fields, **kwargs):
+        self.fields = fields
+        self.init_from_fields(self, fields, kwargs)
+
+    def to_string(self, indentation='', value_mappers={}):
+        return HCI_Object.format_fields(self.__dict__, self.fields, indentation, value_mappers)
+
+    def __str__(self):
+        return self.to_string()
+
+
+# -----------------------------------------------------------------------------
+# Bluetooth Address
+# -----------------------------------------------------------------------------
+class Address:
+    '''
+    Bluetooth Address (see Bluetooth spec Vol 6, Part B - 1.3 DEVICE ADDRESS)
+    NOTE: the address bytes are stored in little-endian byte order here, so
+    address[0] is the LSB of the address, address[5] is the MSB.
+    '''
+
+    PUBLIC_DEVICE_ADDRESS   = 0x00
+    RANDOM_DEVICE_ADDRESS   = 0x01
+    PUBLIC_IDENTITY_ADDRESS = 0x02
+    RANDOM_IDENTITY_ADDRESS = 0x03
+
+    ADDRESS_TYPE_NAMES = {
+        PUBLIC_DEVICE_ADDRESS:   'PUBLIC_DEVICE_ADDRESS',
+        RANDOM_DEVICE_ADDRESS:   'RANDOM_DEVICE_ADDRESS',
+        PUBLIC_IDENTITY_ADDRESS: 'PUBLIC_IDENTITY_ADDRESS',
+        RANDOM_IDENTITY_ADDRESS: 'RANDOM_IDENTITY_ADDRESS'
+    }
+
+    ADDRESS_TYPE_SPEC = {'size': 1, 'mapper': lambda x: Address.address_type_name(x)}
+
+    @staticmethod
+    def address_type_name(address_type):
+        return name_or_number(Address.ADDRESS_TYPE_NAMES, address_type)
+
+    @staticmethod
+    def parse_address(data, offset):
+        # Fix the type to a default value. This is used for parsing type-less Classic addresses
+        return Address.parse_address_with_type(data, offset, Address.PUBLIC_DEVICE_ADDRESS)
+
+    @staticmethod
+    def parse_address_with_type(data, offset, address_type):
+        return offset + 6, Address(data[offset:offset + 6], address_type)
+
+    @staticmethod
+    def parse_address_preceded_by_type(data, offset):
+        address_type = data[offset - 1]
+        return Address.parse_address_with_type(data, offset, address_type)
+
+    def __init__(self, address, address_type = RANDOM_DEVICE_ADDRESS):
+        '''
+        Initialize an instance. `address` may be a byte array in little-endian
+        format, or a hex string in big-endian format (with optional ':'
+        separators between the bytes).
+        If the address is a string suffixed with '/P', `address_type` is ignored and the type
+        is set to PUBLIC_DEVICE_ADDRESS.
+        '''
+        if type(address) is bytes:
+            self.address_bytes = address
+        else:
+            # Check if there's a '/P' type specifier
+            if address.endswith('P'):
+                address_type = Address.PUBLIC_DEVICE_ADDRESS
+                address = address[:-2]
+
+            if len(address) == 12 + 5:
+                # Form with ':' separators
+                address = address.replace(':', '')
+            self.address_bytes = bytes(reversed(bytes.fromhex(address)))
+
+        if len(self.address_bytes) != 6:
+            raise ValueError('invalid address length')
+
+        self.address_type = address_type
+
+    @property
+    def is_public(self):
+        return self.address_type == self.PUBLIC_DEVICE_ADDRESS or self.address_type == self.PUBLIC_IDENTITY_ADDRESS
+
+    @property
+    def is_random(self):
+        return not self.is_public
+
+    @property
+    def is_resolved(self):
+        return self.address_type == self.PUBLIC_IDENTITY_ADDRESS or self.address_type == self.RANDOM_IDENTITY_ADDRESS
+
+    @property
+    def is_resolvable(self):
+        return self.address_type == self.RANDOM_DEVICE_ADDRESS and (self.address_bytes[5] >> 6 == 1)
+
+    @property
+    def is_static(self):
+        return self.is_random and (self.address_bytes[5] >> 6 == 3)
+
+    def to_bytes(self):
+        return self.address_bytes
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __hash__(self):
+        return hash(self.address_bytes)
+
+    def __eq__(self, other):
+        return self.address_bytes == other.address_bytes and self.is_public == other.is_public
+
+    def __str__(self):
+        '''
+        String representation of the address, MSB first
+        '''
+        return ':'.join([f'{x:02X}' for x in reversed(self.address_bytes)])
+
+
+# -----------------------------------------------------------------------------
+class HCI_Packet:
+    '''
+    Abstract Base class for HCI packets
+    '''
+
+    @staticmethod
+    def from_bytes(packet):
+        packet_type = packet[0]
+        if packet_type == HCI_COMMAND_PACKET:
+            return HCI_Command.from_bytes(packet)
+        elif packet_type == HCI_ACL_DATA_PACKET:
+            return HCI_AclDataPacket.from_bytes(packet)
+        elif packet_type == HCI_EVENT_PACKET:
+            return HCI_Event.from_bytes(packet)
+        else:
+            return HCI_CustomPacket(packet)
+
+    def __init__(self, name):
+        self.name = name
+
+    def __repr__(self) -> str:
+        return self.name
+
+
+# -----------------------------------------------------------------------------
+class HCI_CustomPacket(HCI_Packet):
+    def __init__(self, payload):
+        super().__init__('HCI_CUSTOM_PACKET')
+        self.hci_packet_type = payload[0]
+        self.payload         = payload
+
+
+# -----------------------------------------------------------------------------
+class HCI_Command(HCI_Packet):
+    '''
+    See Bluetooth spec @ Vol 2, Part E - 5.4.1 HCI Command Packet
+    '''
+    hci_packet_type = HCI_COMMAND_PACKET
+    command_classes = {}
+
+    @staticmethod
+    def command(fields=[], return_parameters_fields=[]):
+        '''
+        Decorator used to declare and register subclasses
+        '''
+
+        def inner(cls):
+            cls.name = cls.__name__.upper()
+            cls.op_code = key_with_value(HCI_COMMAND_NAMES, cls.name)
+            if cls.op_code is None:
+                raise KeyError('command not found in HCI_COMMAND_NAMES')
+            cls.fields = fields
+            cls.return_parameters_fields = return_parameters_fields
+
+            # Patch the __init__ method to fix the op_code
+            def init(self, parameters=None, **kwargs):
+                return HCI_Command.__init__(self, cls.op_code, parameters, **kwargs)
+            cls.__init__ = init
+
+            # Register a factory for this class
+            HCI_Command.command_classes[cls.op_code] = cls
+
+            return cls
+
+        return inner
+
+    @staticmethod
+    def from_bytes(packet):
+        op_code, length = struct.unpack_from('<HB', packet, 1)
+        parameters = packet[4:]
+        if len(parameters) != length:
+            raise ValueError('invalid packet length')
+
+        # Look for a registered class
+        cls = HCI_Command.command_classes.get(op_code)
+        if cls is None:
+            # No class registered, just use a generic instance
+            return HCI_Command(op_code, parameters)
+
+        # Create a new instance
+        self = cls.__new__(cls)
+        HCI_Command.__init__(self, op_code, parameters)
+        if fields := getattr(self, 'fields', None):
+            HCI_Object.init_from_bytes(self, parameters, 0, fields)
+        return self
+
+    @staticmethod
+    def command_name(op_code):
+        name = HCI_COMMAND_NAMES.get(op_code)
+        if name is not None:
+            return name
+        return f'[OGF=0x{op_code >> 10:02x}, OCF=0x{op_code & 0x3FF:04x}]'
+
+    @classmethod
+    def create_return_parameters(cls, **kwargs):
+        return HCI_Object(cls.return_parameters_fields, **kwargs)
+
+    def __init__(self, op_code, parameters=None, **kwargs):
+        super().__init__(HCI_Command.command_name(op_code))
+        if (fields := getattr(self, 'fields', None)) and kwargs:
+            HCI_Object.init_from_fields(self, fields, kwargs)
+            if parameters is None:
+                parameters = HCI_Object.dict_to_bytes(kwargs, fields)
+        self.op_code    = op_code
+        self.parameters = parameters
+
+    def to_bytes(self):
+        parameters = b'' if self.parameters is None else self.parameters
+        return struct.pack('<BHB', HCI_COMMAND_PACKET, self.op_code, len(parameters)) + parameters
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        result = color(self.name, 'green')
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        else:
+            if self.parameters:
+                result += f': {self.parameters.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('lap',            {'size': 3, 'mapper': HCI_Constant.inquiry_lap_name}),
+    ('inquiry_length', 1),
+    ('num_responses',  1)
+])
+class HCI_Inquiry_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.1 Inquiry Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_Inquiry_Cancel_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.2 Inquiry Cancel Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('bd_addr',                   Address.parse_address),
+    ('packet_type',               2),
+    ('page_scan_repetition_mode', 1),
+    ('reserved',                  1),
+    ('clock_offset',              2),
+    ('allow_role_switch',         1)
+])
+class HCI_Create_Connection_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.5 Create Connection Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('reason',            {'size': 1, 'mapper': HCI_Constant.error_name})
+])
+class HCI_Disconnect_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.6 Disconnect Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('bd_addr', Address.parse_address),
+    ('role',    1)
+])
+class HCI_Accept_Connection_Request_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.8 Accept Connection Request Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('bd_addr',  Address.parse_address),
+    ('link_key', 16)
+])
+class HCI_Link_Key_Request_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.10 Link Key Request Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr', Address.parse_address)
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_Link_Key_Request_Negative_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.11 Link Key Request Negative Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr', Address.parse_address)
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_PIN_Code_Request_Negative_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.13 PIN Code Request Negative Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('packet_type',       2)
+])
+class HCI_Change_Connection_Packet_Type_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.14 Change Connection Packet Type Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_Authentication_Requested_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.15 Authentication Requested Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('encryption_enable', 1)
+])
+class HCI_Set_Connection_Encryption_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.16 Set Connection Encryption Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('bd_addr',                   Address.parse_address),
+    ('page_scan_repetition_mode', 1),
+    ('reserved',                  1),
+    ('clock_offset',              2)
+])
+class HCI_Remote_Name_Request_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.19 Remote Name Request Command
+    '''
+    R0 = 0x00
+    R1 = 0x01
+    R2 = 0x02
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_Read_Remote_Supported_Features_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.21 Read Remote Supported Features Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('page_number',       1)
+])
+class HCI_Read_Remote_Extended_Features_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.22 Read Remote Extended Features Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_Read_Remote_Version_Information_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.23 Read Remote Version Information Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_Read_Clock_Offset_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.23 Read Clock Offset Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr',                     Address.parse_address),
+        ('io_capability',               {'size': 1, 'mapper': HCI_Constant.io_capability_name}),
+        ('oob_data_present',            1),
+        ('authentication_requirements', {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name})
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_IO_Capability_Request_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.29 IO Capability Request Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr', Address.parse_address)
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_User_Confirmation_Request_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.30 User Confirmation Request Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr', Address.parse_address)
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_User_Confirmation_Request_Negative_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.31 User Confirmation Request Negative Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr', Address.parse_address),
+        ('numeric_value', 4)
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_User_Passkey_Request_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.32 User Passkey Request Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr', Address.parse_address)
+    ],
+    return_parameters_fields=[
+        ('status',  STATUS_SPEC),
+        ('bd_addr', Address.parse_address)
+    ]
+)
+class HCI_User_Passkey_Request_Negative_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.33 User Passkey Request Negative Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',                      2),
+    ('transmit_bandwidth',                     4),
+    ('receive_bandwidth',                      4),
+    ('transmit_coding_format',                 5),
+    ('receive_coding_format',                  5),
+    ('transmit_codec_frame_size',              2),
+    ('receive_codec_frame_size',               2),
+    ('input_bandwidth',                        4),
+    ('output_bandwidth',                       4),
+    ('input_coding_format',                    5),
+    ('output_coding_format',                   5),
+    ('input_coded_data_size',                  2),
+    ('output_coded_data_size',                 2),
+    ('input_pcm_data_format',                  1),
+    ('output_pcm_data_format',                 1),
+    ('input_pcm_sample_payload_msb_position',  1),
+    ('output_pcm_sample_payload_msb_position', 1),
+    ('input_data_path',                        1),
+    ('output_data_path',                       1),
+    ('input_transport_unit_size',              1),
+    ('output_transport_unit_size',             1),
+    ('max_latency',                            2),
+    ('packet_type',                            2),
+    ('retransmission_effort',                  1)
+])
+class HCI_Enhanced_Setup_Synchronous_Connection_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.1.45 Enhanced Setup Synchronous Connection Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',  2),
+    ('sniff_max_interval', 2),
+    ('sniff_min_interval', 2),
+    ('sniff_attempt',      2),
+    ('sniff_timeout',      2)
+])
+class HCI_Sniff_Mode_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.2.2 Sniff Mode Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_Exit_Sniff_Mode_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.2.3 Exit Sniff Mode Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('bd_addr', Address.parse_address),
+    ('role',    {'size': 1, 'mapper': HCI_Constant.role_name})
+])
+class HCI_Switch_Role_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.2.8 Switch Role Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',    2),
+    ('link_policy_settings', 2)
+])
+class HCI_Write_Link_Policy_Settings_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.2.10 Write Link Policy Settings Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('default_link_policy_settings', 2)
+])
+class HCI_Write_Default_Link_Policy_Settings_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.2.12 Write Default Link Policy Settings Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',      2),
+    ('maximum_latency',        2),
+    ('minimum_remote_timeout', 2),
+    ('minimum_local_timeout',  2)
+])
+class HCI_Sniff_Subrating_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.2.14 Sniff Subrating Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('event_mask', 8)
+])
+class HCI_Set_Event_Mask_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.1 Set Event Mask Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_Reset_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.2 Reset Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('filter_type',       1),
+    ('filter_condition', '*'),
+])
+class HCI_Set_Event_Filter_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.3 Set Event Filter Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr',       Address.parse_address),
+        ('read_all_flag', 1)
+    ],
+    return_parameters_fields=[
+        ('status',        STATUS_SPEC),
+        ('max_num_keys',  2),
+        ('num_keys_read', 2)
+    ]
+)
+class HCI_Read_Stored_Link_Key_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.8 Read Stored Link Key Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('bd_addr',         Address.parse_address),
+        ('delete_all_flag', 1)
+    ],
+    return_parameters_fields=[
+        ('status',           STATUS_SPEC),
+        ('num_keys_deleted', 2)
+    ]
+)
+class HCI_Delete_Stored_Link_Key_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.10 Delete Stored Link Key Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string})
+])
+class HCI_Write_Local_Name_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.11 Write Local Name Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',     STATUS_SPEC),
+    ('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string})
+])
+class HCI_Read_Local_Name_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.12 Read Local Name Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('conn_accept_timeout', 2)
+])
+class HCI_Write_Connection_Accept_Timeout_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.14 Write Connection Accept Timeout Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('page_timeout', 2)
+])
+class HCI_Write_Page_Timeout_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.16 Write Page Timeout Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('scan_enable', 1)
+])
+class HCI_Write_Scan_Enable_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.18 Write Scan Enable Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',             STATUS_SPEC),
+    ('page_scan_interval', 2),
+    ('page_scan_window',   2)
+])
+class HCI_Read_Page_Scan_Activity_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.19 Read Page Scan Activity Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('page_scan_interval', 2),
+    ('page_scan_window',   2)
+])
+class HCI_Write_Page_Scan_Activity_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.20 Write Page Scan Activity Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('inquiry_scan_interval', 2),
+    ('inquiry_scan_window',   2)
+])
+class HCI_Write_Inquiry_Scan_Activity_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.22 Write Inquiry Scan Activity Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',          STATUS_SPEC),
+    ('class_of_device', {'size': 3, 'mapper': map_class_of_device})
+])
+class HCI_Read_Class_Of_Device_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.25 Read Class of Device Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('class_of_device', {'size': 3, 'mapper': map_class_of_device})
+])
+class HCI_Write_Class_Of_Device_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.26 Write Class of Device Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',        STATUS_SPEC),
+    ('voice_setting', 2)
+])
+class HCI_Read_Voice_Setting_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.27 Read Voice Setting Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('voice_setting', 2)
+])
+class HCI_Write_Voice_Setting_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.28 Write Voice Setting Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+class HCI_Read_Synchronous_Flow_Control_Enable_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.36 Write Synchronous Flow Control Enable Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('synchronous_flow_control_enable', 1)
+])
+class HCI_Write_Synchronous_Flow_Control_Enable_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.37 Write Synchronous Flow Control Enable Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('host_acl_data_packet_length',             2),
+    ('host_synchronous_data_packet_length',     1),
+    ('host_total_num_acl_data_packets',         2),
+    ('host_total_num_synchronous_data_packets', 2)
+])
+class HCI_Host_Buffer_Size_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.39 Host Buffer Size Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('handle',                   2),
+        ('link_supervision_timeout', 2)
+    ],
+    return_parameters_fields=[
+        ('status', STATUS_SPEC),
+        ('handle', 2),
+    ]
+)
+class HCI_Write_Link_Supervision_Timeout_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.42 Write Link Supervision Timeout Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',          STATUS_SPEC),
+    ('num_support_iac', 1)
+])
+class HCI_Read_Number_Of_Supported_IAC_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.43 Read Number Of Supported IAC Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',          STATUS_SPEC),
+    ('num_current_iac', 1),
+    ('iac_lap',         '*')  # TODO: this should be parsed as an array
+])
+class HCI_Read_Current_IAC_LAP_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.44 Read Current IAC LAP Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('scan_type', 1)
+])
+class HCI_Write_Inquiry_Scan_Type_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.48 Write Inquiry Scan Type Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('inquiry_mode', 1)
+])
+class HCI_Write_Inquiry_Mode_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.50 Write Inquiry Mode Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',         STATUS_SPEC),
+    ('page_scan_type', 1)
+])
+class HCI_Read_Page_Scan_Type_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.51 Read Page Scan Type Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('page_scan_type', 1)
+])
+class HCI_Write_Page_Scan_Type_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.52 Write Page Scan Type Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('fec_required',              1),
+    ('extended_inquiry_response', {'size': 240, 'serializer': lambda x: padded_bytes(x, 240)})
+])
+class HCI_Write_Extended_Inquiry_Response_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.56 Write Extended Inquiry Response Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('simple_pairing_mode', 1)
+])
+class HCI_Write_Simple_Pairing_Mode_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.59 Write Simple Pairing Mode Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',   STATUS_SPEC),
+    ('tx_power', -1)
+])
+class HCI_Read_Inquiry_Response_Transmit_Power_Level_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.61 Read Inquiry Response Transmit Power Level Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',                   STATUS_SPEC),
+    ('erroneous_data_reporting', 1)
+])
+class HCI_Read_Default_Erroneous_Data_Reporting_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.64 Read Default Erroneous Data Reporting Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('event_mask_page_2', 8)
+])
+class HCI_Set_Event_Mask_Page_2_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.69 Set Event Mask Page 2 Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_Read_LE_Host_Support_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.78 Read LE Host Support Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('le_supported_host',    1),
+    ('simultaneous_le_host', 1)
+])
+class HCI_Write_LE_Host_Support_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.79 Write LE Host Support Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('secure_connections_host_support', 1)
+])
+class HCI_Write_Secure_Connections_Host_Support_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.92 Write Secure Connections Host Support Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',             2),
+    ('authenticated_payload_timeout', 2)
+])
+class HCI_Write_Authenticated_Payload_Timeout_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.3.94 Write Authenticated Payload Timeout Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',             STATUS_SPEC),
+    ('hci_version',        1),
+    ('hci_revsion',        2),
+    ('lmp_pal_version',    1),
+    ('manufacturer_name',  2),
+    ('lmp_pal_subversion', 2)
+])
+class HCI_Read_Local_Version_Information_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.1 Read Local Version Information Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',             STATUS_SPEC),
+    ('supported_commands', 64)
+])
+class HCI_Read_Local_Supported_Commands_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.2 Read Local Supported Commands Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_Read_Local_Supported_Features_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.3 Read Local Supported Features Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('page_number', 1)
+    ],
+    return_parameters_fields=[
+        ('status',                STATUS_SPEC),
+        ('page_number',           1),
+        ('maximum_page_number',   1),
+        ('extended_lmp_features', 8)
+    ]
+)
+class HCI_Read_Local_Extended_Features_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.4 Read Local Extended Features Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',                                STATUS_SPEC),
+    ('hc_acl_data_packet_length',             2),
+    ('hc_synchronous_data_packet_length',     1),
+    ('hc_total_num_acl_data_packets',         2),
+    ('hc_total_num_synchronous_data_packets', 2)
+])
+class HCI_Read_Buffer_Size_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.5 Read Buffer Size Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',  STATUS_SPEC),
+    ('bd_addr', Address.parse_address)
+])
+class HCI_Read_BD_ADDR_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.6 Read BD_ADDR Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_Read_Local_Supported_Codecs_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+    fields=[
+        ('connection_handle', 2)
+    ],
+    return_parameters_fields=[
+        ('status',            STATUS_SPEC),
+        ('connection_handle', 2),
+        ('key_size',          1)
+    ]
+)
+class HCI_Read_Encryption_Key_Size_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.5.7 Read Encryption Key Size Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('le_event_mask', 8)
+])
+class HCI_LE_Set_Event_Mask_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.1 LE Set Event Mask Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(return_parameters_fields=[
+    ('status',                           STATUS_SPEC),
+    ('hc_le_acl_data_packet_length',     2),
+    ('hc_total_num_le_acl_data_packets', 1)
+])
+class HCI_LE_Read_Buffer_Size_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.2 LE Read Buffer Size Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('random_address', lambda data, offset: Address.parse_address_with_type(data, offset, Address.RANDOM_DEVICE_ADDRESS))
+])
+class HCI_LE_Set_Random_Address_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.4 LE Set Random Address Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('advertising_interval_min',  2),
+    ('advertising_interval_max',  2),
+    ('advertising_type',          {'size': 1, 'mapper': lambda x: HCI_LE_Set_Advertising_Parameters_Command.advertising_type_name(x)}),
+    ('own_address_type',          Address.ADDRESS_TYPE_SPEC),
+    ('peer_address_type',         Address.ADDRESS_TYPE_SPEC),
+    ('peer_address',              Address.parse_address_preceded_by_type),
+    ('advertising_channel_map',   1),
+    ('advertising_filter_policy', 1),
+])
+class HCI_LE_Set_Advertising_Parameters_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.5 LE Set Advertising Parameters Command
+    '''
+
+    ADV_IND         = 0x00
+    ADV_DIRECT_IND  = 0x01
+    ADV_SCAN_IND    = 0x02
+    ADV_NONCONN_IND = 0x03
+    ADV_DIRECT_IND  = 0x04
+
+    ADVERTISING_TYPE_NAMES = {
+        ADV_IND:         'ADV_IND',
+        ADV_DIRECT_IND:  'ADV_DIRECT_IND',
+        ADV_SCAN_IND:    'ADV_SCAN_IND',
+        ADV_NONCONN_IND: 'ADV_NONCONN_IND',
+        ADV_DIRECT_IND:  'ADV_DIRECT_IND'
+    }
+
+    @classmethod
+    def advertising_type_name(cls, advertising_type):
+        return name_or_number(cls.ADVERTISING_TYPE_NAMES, advertising_type)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_LE_Read_Advertising_Channel_Tx_Power_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.6 LE Read Advertising Channel Tx Power Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('advertising_data', {
+        'parser':     HCI_Object.parse_length_prefixed_bytes,
+        'serializer': functools.partial(HCI_Object.serialize_length_prefixed_bytes, padded_size=32)
+    })
+])
+class HCI_LE_Set_Advertising_Data_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.7 LE Set Advertising Data Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('scan_response_data', {
+        'parser':     HCI_Object.parse_length_prefixed_bytes,
+        'serializer': functools.partial(HCI_Object.serialize_length_prefixed_bytes, padded_size=32)
+    })
+])
+class HCI_LE_Set_Scan_Response_Data_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.8 LE Set Scan Response Data Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('advertising_enable', 1)
+])
+class HCI_LE_Set_Advertising_Enable_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.9 LE Set Advertising Enable Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('le_scan_type',           1),
+    ('le_scan_interval',       2),
+    ('le_scan_window',         2),
+    ('own_address_type',       Address.ADDRESS_TYPE_SPEC),
+    ('scanning_filter_policy', 1)
+])
+class HCI_LE_Set_Scan_Parameters_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.10 LE Set Scan Parameters Command
+    '''
+    PASSIVE_SCANNING = 0
+    ACTIVE_SCANNING  = 1
+
+    BASIC_UNFILTERED_POLICY    = 0x00
+    BASIC_FILTERED_POLICY      = 0x01
+    EXTENDED_UNFILTERED_POLICY = 0x02
+    EXTENDED_FILTERED_POLICY   = 0x03
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('le_scan_enable',    1),
+    ('filter_duplicates', 1),
+])
+class HCI_LE_Set_Scan_Enable_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.11 LE Set Scan Enable Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('le_scan_interval',        2),
+    ('le_scan_window',          2),
+    ('initiator_filter_policy', 1),
+    ('peer_address_type',       Address.ADDRESS_TYPE_SPEC),
+    ('peer_address',            Address.parse_address_preceded_by_type),
+    ('own_address_type',        Address.ADDRESS_TYPE_SPEC),
+    ('conn_interval_min',       2),
+    ('conn_interval_max',       2),
+    ('conn_latency',            2),
+    ('supervision_timeout',     2),
+    ('minimum_ce_length',       2),
+    ('maximum_ce_length',       2)
+])
+class HCI_LE_Create_Connection_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.12 LE Create Connection Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_LE_Create_Connection_Cancel_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.13 LE Create Connection Cancel Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_LE_Read_White_List_Size_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.14 LE Read White List Size Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_LE_Clear_White_List_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.15 LE Clear White List Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('address_type', Address.ADDRESS_TYPE_SPEC),
+    ('address',      Address.parse_address_preceded_by_type)
+])
+class HCI_LE_Add_Device_To_White_List_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.16 LE Add Device To White List Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('address_type', Address.ADDRESS_TYPE_SPEC),
+    ('address',      Address.parse_address_preceded_by_type)
+])
+class HCI_LE_Remove_Device_From_White_List_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.17 LE Remove Device From White List Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',   2),
+    ('conn_interval_min',   2),
+    ('conn_interval_max',   2),
+    ('conn_latency',        2),
+    ('supervision_timeout', 2),
+    ('minimum_ce_length',   2),
+    ('maximum_ce_length',   2)
+])
+class HCI_LE_Connection_Update_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.18 LE Connection Update Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_LE_Read_Remote_Features_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.21 LE Read Remote Features Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle',     2),
+    ('random_number',         8),
+    ('encrypted_diversifier', 2),
+    ('long_term_key',         16)
+])
+class HCI_LE_Start_Encryption_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.24 LE Start Encryption Command
+    (renamed to "LE Enable Encryption Command" in version 5.2 of the specification)
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('long_term_key',     16)
+])
+class HCI_LE_Long_Term_Key_Request_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.25 LE Long Term Key Request Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2)
+])
+class HCI_LE_Long_Term_Key_Request_Negative_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.26 LE Long Term Key Request Negative Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_LE_Read_Supported_States_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.27 LE Read Supported States Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('interval_min',      2),
+    ('interval_max',      2),
+    ('latency',           2),
+    ('timeout',           2),
+    ('minimum_ce_length', 2),
+    ('maximum_ce_length', 2)
+])
+class HCI_LE_Remote_Connection_Parameter_Request_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.31 LE Remote Connection Parameter Request Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('connection_handle', 2),
+    ('reason',            {'size': 1, 'mapper': HCI_Constant.error_name})
+])
+class HCI_LE_Remote_Connection_Parameter_Request_Negative_Reply_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.32 LE Remote Connection Parameter Request Negative Reply Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('suggested_max_tx_octets', 2),
+    ('suggested_max_tx_time',   2)
+])
+class HCI_LE_Write_Suggested_Default_Data_Length_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.35 LE Write Suggested Default Data Length Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('peer_identity_address_type', Address.ADDRESS_TYPE_SPEC),
+    ('peer_identity_address',      Address.parse_address_preceded_by_type),
+    ('peer_irk',                   16),
+    ('local_irk',                  16),
+])
+class HCI_LE_Add_Device_To_Resolving_List_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.38 LE Add Device To Resolving List Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command()
+class HCI_LE_Clear_Resolving_List_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.40 LE Clear Resolving List Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('all_phys', 1),
+    ('tx_phys',  1),
+    ('rx_phys',  1)
+])
+class HCI_LE_Set_Default_PHY_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.48 LE Set Default PHY Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('address_resolution_enable', 1)
+])
+class HCI_LE_Set_Address_Resolution_Enable_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.44 LE Set Address Resolution Enable Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([
+    ('rpa_timeout', 2)
+])
+class HCI_LE_Set_Resolvable_Private_Address_Timeout_Command(HCI_Command):
+    '''
+    See Bluetooth spec @ 7.8.45 LE Set Resolvable Private Address Timeout Command
+    '''
+
+
+# -----------------------------------------------------------------------------
+# HCI Events
+# -----------------------------------------------------------------------------
+class HCI_Event(HCI_Packet):
+    '''
+    See Bluetooth spec @ Vol 2, Part E - 5.4.4 HCI Event Packet
+    '''
+    hci_packet_type    = HCI_EVENT_PACKET
+    event_classes      = {}
+    meta_event_classes = {}
+
+    @staticmethod
+    def event(fields=[]):
+        '''
+        Decorator used to declare and register subclasses
+        '''
+
+        def inner(cls):
+            cls.name = cls.__name__.upper()
+            cls.event_code = key_with_value(HCI_EVENT_NAMES, cls.name)
+            if cls.event_code is None:
+                raise KeyError('event not found in HCI_EVENT_NAMES')
+            cls.fields = fields
+
+            # Patch the __init__ method to fix the event_code
+            def init(self, parameters=None, **kwargs):
+                return HCI_Event.__init__(self, cls.event_code, parameters, **kwargs)
+            cls.__init__ = init
+
+            # Register a factory for this class
+            HCI_Event.event_classes[cls.event_code] = cls
+
+            return cls
+
+        return inner
+
+    @staticmethod
+    def registered(cls):
+        cls.name = cls.__name__.upper()
+        cls.event_code = key_with_value(HCI_EVENT_NAMES, cls.name)
+        if cls.event_code is None:
+            raise KeyError('event not found in HCI_EVENT_NAMES')
+
+        # Register a factory for this class
+        HCI_Event.event_classes[cls.event_code] = cls
+
+        return cls
+
+    @staticmethod
+    def from_bytes(packet):
+        event_code = packet[1]
+        length     = packet[2]
+        parameters = packet[3:]
+        if len(parameters) != length:
+            raise ValueError('invalid packet length')
+
+        if event_code == HCI_LE_META_EVENT:
+            # We do this dispatch here and not in the subclass in order to avoid call loops
+            subevent_code = parameters[0]
+            cls = HCI_Event.meta_event_classes.get(subevent_code)
+            if cls is None:
+                # No class registered, just use a generic class instance
+                return HCI_LE_Meta_Event(subevent_code, parameters)
+
+        else:
+            cls = HCI_Event.event_classes.get(event_code)
+            if cls is None:
+                # No class registered, just use a generic class instance
+                return HCI_Event(event_code, parameters)
+
+        # Invoke the factory to create a new instance
+        return cls.from_parameters(parameters)
+
+    @classmethod
+    def from_parameters(cls, parameters):
+        self = cls.__new__(cls)
+        HCI_Event.__init__(self, self.event_code, parameters)
+        if fields := getattr(self, 'fields', None):
+            HCI_Object.init_from_bytes(self, parameters, 0, fields)
+        return self
+
+    @staticmethod
+    def event_name(event_code):
+        return name_or_number(HCI_EVENT_NAMES, event_code)
+
+    def __init__(self, event_code, parameters=None, **kwargs):
+        super().__init__(HCI_Event.event_name(event_code))
+        if (fields := getattr(self, 'fields', None)) and kwargs:
+            HCI_Object.init_from_fields(self, fields, kwargs)
+            if parameters is None:
+                parameters = HCI_Object.dict_to_bytes(kwargs, fields)
+        self.event_code = event_code
+        self.parameters = parameters
+
+    def to_bytes(self):
+        parameters = b'' if self.parameters is None else self.parameters
+        return bytes([HCI_EVENT_PACKET, self.event_code, len(parameters)]) + parameters
+
+    def __str__(self):
+        result = color(self.name, 'magenta')
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        else:
+            if self.parameters:
+                result += f': {self.parameters.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+class HCI_LE_Meta_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.65 LE Meta Event
+    '''
+
+    @staticmethod
+    def event(fields=[]):
+        '''
+        Decorator used to declare and register subclasses
+        '''
+
+        def inner(cls):
+            cls.name = cls.__name__.upper()
+            cls.subevent_code = key_with_value(HCI_SUBEVENT_NAMES, cls.name)
+            if cls.subevent_code is None:
+                raise KeyError('subevent not found in HCI_SUBEVENT_NAMES')
+            cls.fields = fields
+
+            # Patch the __init__ method to fix the subevent_code
+            def init(self, parameters=None, **kwargs):
+                return HCI_LE_Meta_Event.__init__(self, cls.subevent_code, parameters, **kwargs)
+            cls.__init__ = init
+
+            # Register a factory for this class
+            HCI_Event.meta_event_classes[cls.subevent_code] = cls
+
+            return cls
+
+        return inner
+
+    @classmethod
+    def from_parameters(cls, parameters):
+        self = cls.__new__(cls)
+        HCI_LE_Meta_Event.__init__(self, self.subevent_code, parameters)
+        if fields := getattr(self, 'fields', None):
+            HCI_Object.init_from_bytes(self, parameters, 1, fields)
+        return self
+
+    @staticmethod
+    def subevent_name(subevent_code):
+        return name_or_number(HCI_SUBEVENT_NAMES, subevent_code)
+
+    def __init__(self, subevent_code, parameters, **kwargs):
+        self.subevent_code = subevent_code
+        if parameters is None and (fields := getattr(self, 'fields', None)) and kwargs:
+            parameters = bytes([subevent_code]) + HCI_Object.dict_to_bytes(kwargs, fields)
+        super().__init__(HCI_LE_META_EVENT, parameters, **kwargs)
+
+        # Override the name in order to adopt the subevent name instead
+        self.name = self.subevent_name(subevent_code)
+
+    def __str__(self):
+        result = color(self.subevent_name(self.subevent_code), 'magenta')
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        else:
+            if self.parameters:
+                result += f': {self.parameters.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('status',                STATUS_SPEC),
+    ('connection_handle',     2),
+    ('role',                  {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}),
+    ('peer_address_type',     Address.ADDRESS_TYPE_SPEC),
+    ('peer_address',          Address.parse_address_preceded_by_type),
+    ('conn_interval',         2),
+    ('conn_latency',          2),
+    ('supervision_timeout',   2),
+    ('master_clock_accuracy', 1)
+])
+class HCI_LE_Connection_Complete_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.1 LE Connection Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+class HCI_LE_Advertising_Report_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.2 LE Advertising Report Event
+    '''
+    subevent_code = HCI_LE_ADVERTISING_REPORT_EVENT
+
+    # Event Types
+    ADV_IND         = 0x00
+    ADV_DIRECT_IND  = 0x01
+    ADV_SCAN_IND    = 0x02
+    ADV_NONCONN_IND = 0x03
+    SCAN_RSP        = 0x04
+
+    EVENT_TYPE_NAMES = {
+        ADV_IND:         'ADV_IND',          # Connectable and scannable undirected advertising
+        ADV_DIRECT_IND:  'ADV_DIRECT_IND',   # Connectable directed advertising
+        ADV_SCAN_IND:    'ADV_SCAN_IND',     # Scannable undirected advertising
+        ADV_NONCONN_IND: 'ADV_NONCONN_IND',  # Non connectable undirected advertising
+        SCAN_RSP:        'SCAN_RSP'          # Scan Response
+    }
+
+    REPORT_FIELDS = [
+        ('event_type',   1),
+        ('address_type', Address.ADDRESS_TYPE_SPEC),
+        ('address',      Address.parse_address_preceded_by_type),
+        ('data',         {'parser': HCI_Object.parse_length_prefixed_bytes, 'serializer': HCI_Object.serialize_length_prefixed_bytes}),
+        ('rssi',         -1)
+    ]
+
+    @classmethod
+    def event_type_name(cls, event_type):
+        return name_or_number(cls.EVENT_TYPE_NAMES, event_type)
+
+    @staticmethod
+    def from_parameters(parameters):
+        num_reports = parameters[1]
+        reports = []
+        offset = 2
+        for _ in range(num_reports):
+            report = HCI_Object.from_bytes(parameters, offset, HCI_LE_Advertising_Report_Event.REPORT_FIELDS)
+            offset += 10 + len(report.data)
+            reports.append(report)
+
+        return HCI_LE_Advertising_Report_Event(reports)
+
+    def __init__(self, reports):
+        self.reports = reports[:]
+
+        # Serialize the fields
+        parameters = bytes([HCI_LE_ADVERTISING_REPORT_EVENT, len(reports)]) + b''.join([bytes(report) for report in reports])
+
+        super().__init__(self.subevent_code, parameters)
+
+    def __str__(self):
+        reports = '\n'.join([report.to_string('  ', {
+            'event_type':   self.event_type_name,
+            'address_type': Address.address_type_name,
+            'data': lambda x: str(AdvertisingData.from_bytes(x))
+        }) for report in self.reports])
+        return f'{color(self.subevent_name(self.subevent_code), "magenta")}:\n{reports}'
+
+
+HCI_Event.meta_event_classes[HCI_LE_ADVERTISING_REPORT_EVENT] = HCI_LE_Advertising_Report_Event
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('status',              STATUS_SPEC),
+    ('connection_handle',   2),
+    ('conn_interval',       2),
+    ('conn_latency',        2),
+    ('supervision_timeout', 2)
+])
+class HCI_LE_Connection_Update_Complete_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.3 LE Connection Update Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('le_features',       8)
+])
+class HCI_LE_Read_Remote_Features_Complete_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.4 LE Read Remote Features Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('connection_handle',     2),
+    ('random_number',         8),
+    ('encryption_diversifier', 2)
+])
+class HCI_LE_Long_Term_Key_Request_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.5 LE Long Term Key Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('connection_handle', 2),
+    ('interval_min',      2),
+    ('interval_max',      2),
+    ('latency',           2),
+    ('timeout',           2)
+])
+class HCI_LE_Remote_Connection_Parameter_Request_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.6 LE Remote Connection Parameter Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('connection_handle', 2),
+    ('max_tx_octets',     2),
+    ('max_tx_time',       2),
+    ('max_rx_octets',     2),
+    ('max_rx_time',       2),
+])
+class HCI_LE_Data_Length_Change_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.7 LE Data Length Change Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('status',                           STATUS_SPEC),
+    ('connection_handle',                2),
+    ('role',                             {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}),
+    ('peer_address_type',                Address.ADDRESS_TYPE_SPEC),
+    ('peer_address',                     Address.parse_address_preceded_by_type),
+    ('local_resolvable_private_address', Address.parse_address),
+    ('peer_resolvable_private_address',  Address.parse_address),
+    ('conn_interval',                    2),
+    ('conn_latency',                     2),
+    ('supervision_timeout',              2),
+    ('master_clock_accuracy',            1)
+])
+class HCI_LE_Enhanced_Connection_Complete_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.10 LE Enhanced Connection Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('tx_phy',            {'size': 1, 'mapper': HCI_Constant.le_phy_name}),
+    ('rx_phy',            {'size': 1, 'mapper': HCI_Constant.le_phy_name})
+])
+class HCI_LE_PHY_Update_Complete_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.12 LE PHY Update Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event([
+    ('connection_handle',           2),
+    ('channel_selection_algorithm', 1)
+])
+class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event):
+    '''
+    See Bluetooth spec @ 7.7.65.20 LE Channel Selection Algorithm Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status', STATUS_SPEC)
+])
+class HCI_Inquiry_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.1 Inquiry Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.registered
+class HCI_Inquiry_Result_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.2 Inquiry Result Event
+    '''
+
+    RESPONSE_FIELDS = [
+        ('bd_addr',                   Address.parse_address),
+        ('page_scan_repetition_mode', 1),
+        ('reserved',                  1),
+        ('reserved',                  1),
+        ('class_of_device',           {'size': 3, 'mapper': map_class_of_device}),
+        ('clock_offset',              2)
+    ]
+
+    @staticmethod
+    def from_parameters(parameters):
+        num_responses = parameters[0]
+        responses = []
+        offset = 1
+        for _ in range(num_responses):
+            response = HCI_Object.from_bytes(parameters, offset, HCI_Inquiry_Result_Event.RESPONSE_FIELDS)
+            offset += 14
+            responses.append(response)
+
+        return HCI_Inquiry_Result_Event(responses)
+
+    def __init__(self, responses):
+        self.responses = responses[:]
+
+        # Serialize the fields
+        parameters = bytes([HCI_INQUIRY_RESULT_EVENT, len(responses)]) + b''.join([bytes(response) for response in responses])
+
+        super().__init__(HCI_INQUIRY_RESULT_EVENT, parameters)
+
+    def __str__(self):
+        responses = '\n'.join([response.to_string(indentation='  ') for response in self.responses])
+        return f'{color("HCI_INQUIRY_RESULT_EVENT", "magenta")}:\n{responses}'
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',             STATUS_SPEC),
+    ('connection_handle',  2),
+    ('bd_addr',            Address.parse_address),
+    ('link_type',          {'size': 1, 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x)}),
+    ('encryption_enabled', 1)
+])
+class HCI_Connection_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.3 Connection Complete Event
+    '''
+
+    SCO_LINK_TYPE  = 0x00
+    ACL_LINK_TYPE  = 0x01
+    ESCO_LINK_TYPE = 0x02
+
+    LINK_TYPE_NAMES = {
+        SCO_LINK_TYPE:  'SCO',
+        ACL_LINK_TYPE:  'ACL',
+        ESCO_LINK_TYPE: 'eSCO'
+    }
+
+    @staticmethod
+    def link_type_name(link_type):
+        return name_or_number(HCI_Connection_Complete_Event.LINK_TYPE_NAMES, link_type)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',         Address.parse_address),
+    ('class_of_device', 3),
+    ('link_type',       {'size': 1, 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x)})
+])
+class HCI_Connection_Request_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.4 Connection Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('reason',            {'size': 1, 'mapper': HCI_Constant.error_name})
+])
+class HCI_Disconnection_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.5 Disconnection Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2)
+])
+class HCI_Authentication_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.6 Authentication Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',      STATUS_SPEC),
+    ('bd_addr',     Address.parse_address),
+    ('remote_name', {'size': 248, 'mapper': map_null_terminated_utf8_string})
+])
+class HCI_Remote_Name_Request_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.7 Remote Name Request Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',             STATUS_SPEC),
+    ('connection_handle',  2),
+    ('encryption_enabled', {'size': 1, 'mapper': lambda x: HCI_Encryption_Change_Event.encryption_enabled_name(x)})
+])
+class HCI_Encryption_Change_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.8 Encryption Change Event
+    '''
+
+    OFF           = 0x00
+    E0_OR_AES_CCM = 0x01
+    AES_CCM       = 0x02
+
+    ENCYRPTION_ENABLED_NAMES = {
+        OFF:           'OFF',
+        E0_OR_AES_CCM: 'E0_OR_AES_CCM',
+        AES_CCM:       'AES_CCM'
+    }
+
+    @staticmethod
+    def encryption_enabled_name(encryption_enabled):
+        return name_or_number(HCI_Encryption_Change_Event.ENCYRPTION_ENABLED_NAMES, encryption_enabled)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('lmp_features',      8)
+])
+class HCI_Read_Remote_Supported_Features_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.11 Read Remote Supported Features Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('version',           1),
+    ('manufacturer_name', 2),
+    ('subversion',        2)
+])
+class HCI_Read_Remote_Version_Information_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.12 Read Remote Version Information Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('num_hci_command_packets', 1),
+    ('command_opcode',          {'size': 2, 'mapper': HCI_Command.command_name}),
+    ('return_parameters',       '*')
+])
+class HCI_Command_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.14 Command Complete Event
+    '''
+
+    def map_return_parameters(self, return_parameters):
+        # Map simple 'status' return parameters to their named constant form
+        if type(return_parameters) is bytes and len(return_parameters) == 1:
+            # Byte-array form
+            return HCI_Constant.status_name(return_parameters[0])
+        elif type(return_parameters) is int:
+            # Already converted to an integer status code
+            return HCI_Constant.status_name(return_parameters)
+        else:
+            return return_parameters
+
+    @staticmethod
+    def from_parameters(parameters):
+        self = HCI_Command_Complete_Event.__new__(HCI_Command_Complete_Event)
+        HCI_Event.__init__(self, self.event_code, parameters)
+        HCI_Object.init_from_bytes(self, parameters, 0, HCI_Command_Complete_Event.fields)
+
+        # Parse the return parameters
+        if type(self.return_parameters) is bytes and len(self.return_parameters) == 1:
+            # All commands with 1-byte return parameters return a 'status' field, convert it to an integer
+            self.return_parameters = self.return_parameters[0]
+        else:
+            cls = HCI_Command.command_classes.get(self.command_opcode)
+            if cls and cls.return_parameters_fields:
+                self.return_parameters = HCI_Object.from_bytes(self.return_parameters, 0, cls.return_parameters_fields)
+                self.return_parameters.fields = cls.return_parameters_fields
+
+        return self
+
+    def __str__(self):
+        return f'{color(self.name, "magenta")}:\n' + HCI_Object.format_fields(self.__dict__, self.fields, '  ', {
+            'return_parameters': self.map_return_parameters
+        })
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',                  {'size': 1, 'mapper': lambda x: HCI_Command_Status_Event.status_name(x)}),
+    ('num_hci_command_packets', 1),
+    ('command_opcode',          {'size': 2, 'mapper': HCI_Command.command_name})
+])
+class HCI_Command_Status_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.15 Command Complete Event
+    '''
+    PENDING = 0
+
+    @staticmethod
+    def status_name(status):
+        if status == HCI_Command_Status_Event.PENDING:
+            return 'PENDING'
+        else:
+            return HCI_Constant.error_name(status)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',   STATUS_SPEC),
+    ('bd_addr',  Address.parse_address),
+    ('new_role', {'size': 1, 'mapper': HCI_Constant.role_name})
+])
+class HCI_Role_Change_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.18 Role Change Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.registered
+class HCI_Number_Of_Completed_Packets_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.19 Number Of Completed Packets Event
+    '''
+
+    @classmethod
+    def from_parameters(cls, parameters):
+        self = cls.__new__(cls)
+        self.parameters = parameters
+        num_handles = parameters[0]
+        self.connection_handles = []
+        self.num_completed_packets = []
+        for i in range(num_handles):
+            self.connection_handles.append(
+                struct.unpack_from('<H', parameters, 1 + i * 4)[0]
+            )
+            self.num_completed_packets.append(
+                struct.unpack_from('<H', parameters, 1 + i * 4 + 2)[0]
+            )
+
+        return self
+
+    def __init__(self, connection_handle_and_completed_packets_list):
+        self.connection_handles = []
+        self.num_completed_packets = []
+        parameters = bytes([len(connection_handle_and_completed_packets_list)])
+        for handle, completed_packets in connection_handle_and_completed_packets_list:
+            self.connection_handles.append(handle)
+            self.num_completed_packets.append(completed_packets)
+            parameters += struct.pack('<H', handle)
+            parameters += struct.pack('<H', completed_packets)
+        super().__init__(HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT, parameters)
+
+    def __str__(self):
+        lines = [
+            color(self.name, 'magenta') + ':',
+            color('  number_of_handles:         ', 'cyan') + f'{len(self.connection_handles)}'
+        ]
+        for i in range(len(self.connection_handles)):
+            lines.append(color(f'  connection_handle[{i}]:     ', 'cyan') + f'{self.connection_handles[i]}')
+            lines.append(color(f'  num_completed_packets[{i}]: ', 'cyan') + f'{self.num_completed_packets[i]}')
+        return '\n'.join(lines)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('current_mode',      {'size': 1, 'mapper': lambda x: HCI_Mode_Change_Event.mode_name(x)}),
+    ('interval',          2)
+])
+class HCI_Mode_Change_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.20 Mode Change Event
+    '''
+
+    ACTIVE_MODE = 0x00
+    HOLD_MODE   = 0x01
+    SNIFF_MODE  = 0x02
+
+    MODE_NAMES = {
+        ACTIVE_MODE: 'ACTIVE_MODE',
+        HOLD_MODE:   'HOLD_MODE',
+        SNIFF_MODE:  'SNIFF_MODE'
+    }
+
+    @staticmethod
+    def mode_name(mode):
+        return name_or_number(HCI_Mode_Change_Event.MODE_NAMES, mode)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr', Address.parse_address)
+])
+class HCI_PIN_Code_Request_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.22 PIN Code Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',  Address.parse_address)
+])
+class HCI_Link_Key_Request_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.24 7.7.23 Link Key Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',  Address.parse_address),
+    ('link_key', 16),
+    ('key_type', {'size': 1, 'mapper': HCI_Constant.link_key_type_name})
+])
+class HCI_Link_Key_Notification_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.24 Link Key Notification Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('connection_handle', 2),
+    ('lmp_max_slots',     1)
+])
+class HCI_Max_Slots_Change_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.27 Max Slots Change Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('clock_offset',      2)
+])
+class HCI_Read_Clock_Offset_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.28 Read Clock Offset Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2),
+    ('packet_type',       2)
+])
+class HCI_Connection_Packet_Type_Changed_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.29 Connection Packet Type Changed Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',                   Address.parse_address),
+    ('page_scan_repetition_mode', 1)
+])
+class HCI_Page_Scan_Repetition_Mode_Change_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.31 Page Scan Repetition Mode Change Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.registered
+class HCI_Inquiry_Result_With_Rssi_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.33 Inquiry Result with RSSI Event
+    '''
+
+    RESPONSE_FIELDS = [
+        ('bd_addr',                   Address.parse_address),
+        ('page_scan_repetition_mode', 1),
+        ('reserved',                  1),
+        ('class_of_device',           {'size': 3, 'mapper': map_class_of_device}),
+        ('clock_offset',              2),
+        ('rssi',                      -1)
+    ]
+
+    @staticmethod
+    def from_parameters(parameters):
+        num_responses = parameters[0]
+        responses = []
+        offset = 1
+        for _ in range(num_responses):
+            response = HCI_Object.from_bytes(parameters, offset, HCI_Inquiry_Result_With_Rssi_Event.RESPONSE_FIELDS)
+            offset += 14
+            responses.append(response)
+
+        return HCI_Inquiry_Result_With_Rssi_Event(responses)
+
+    def __init__(self, responses):
+        self.responses = responses[:]
+
+        # Serialize the fields
+        parameters = bytes([HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, len(responses)]) + b''.join([bytes(response) for response in responses])
+
+        super().__init__(HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, parameters)
+
+    def __str__(self):
+        responses = '\n'.join([response.to_string(indentation='  ') for response in self.responses])
+        return f'{color("HCI_INQUIRY_RESULT_WITH_RSSI_EVENT", "magenta")}:\n{responses}'
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',                STATUS_SPEC),
+    ('connection_handle',     2),
+    ('page_number',           1),
+    ('maximum_page_number',   1),
+    ('extended_lmp_features', 8)
+])
+class HCI_Read_Remote_Extended_Features_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.34 Read Remote Extended Features Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',                STATUS_SPEC),
+    ('connection_handle',     2),
+    ('bd_addr',               Address.parse_address),
+    ('link_type',             {'size': 1, 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.link_type_name(x)}),
+    ('transmission_interval', 1),
+    ('retransmission_window', 1),
+    ('rx_packet_length',      2),
+    ('tx_packet_length',      2),
+    ('air_mode',              {'size': 1, 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.air_mode_name(x)}),
+])
+class HCI_Synchronous_Connection_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.35 Synchronous Connection Complete Event
+    '''
+
+    SCO_CONNECTION_LINK_TYPE = 0x00
+    ESCO_CONNECTION_LINK_TYPE = 0x02
+
+    LINK_TYPE_NAMES = {
+        SCO_CONNECTION_LINK_TYPE:  'SCO',
+        ESCO_CONNECTION_LINK_TYPE: 'eSCO'
+    }
+
+    U_LAW_LOG_AIR_MODE        = 0x00
+    A_LAW_LOG_AIR_MORE        = 0x01
+    CVSD_AIR_MODE             = 0x02
+    TRANSPARENT_DATA_AIR_MODE = 0x03
+
+    AIR_MODE_NAMES = {
+        U_LAW_LOG_AIR_MODE:        'u-law log',
+        A_LAW_LOG_AIR_MORE:        'A-law log',
+        CVSD_AIR_MODE:             'CVSD',
+        TRANSPARENT_DATA_AIR_MODE: 'Transparend Data'
+    }
+
+    @staticmethod
+    def link_type_name(link_type):
+        return name_or_number(HCI_Synchronous_Connection_Complete_Event.LINK_TYPE_NAMES, link_type)
+
+    @staticmethod
+    def air_mode_name(air_mode):
+        return name_or_number(HCI_Synchronous_Connection_Complete_Event.AIR_MODE_NAMES, air_mode)
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',                STATUS_SPEC),
+    ('connection_handle',     2),
+    ('transmission_interval', 1),
+    ('retransmission_window', 1),
+    ('rx_packet_length',      2),
+    ('tx_packet_length',      2)
+])
+class HCI_Synchronous_Connection_Changed_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.36 Synchronous Connection Changed Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('num_responses',             1),
+    ('bd_addr',                   Address.parse_address),
+    ('page_scan_repetition_mode', 1),
+    ('reserved',                  1),
+    ('class_of_device',          {'size': 3, 'mapper': map_class_of_device}),
+    ('clock_offset',              2),
+    ('rssi',                     -1),
+    ('extended_inquiry_response', 240),
+])
+class HCI_Extended_Inquiry_Result_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.38 Extended Inquiry Result Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',            STATUS_SPEC),
+    ('connection_handle', 2)
+])
+class HCI_Encryption_Key_Refresh_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.39 Encryption Key Refresh Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr', Address.parse_address)
+])
+class HCI_IO_Capability_Request_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.40 IO Capability Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',                     Address.parse_address),
+    ('io_capability',               {'size': 1, 'mapper': HCI_Constant.io_capability_name}),
+    ('oob_data_present',            1),
+    ('authentication_requirements', {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name})
+])
+class HCI_IO_Capability_Response_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.41 IO Capability Response Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',       Address.parse_address),
+    ('numeric_value', 4)
+])
+class HCI_User_Confirmation_Request_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.42 User Confirmation Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr', Address.parse_address)
+])
+class HCI_User_Passkey_Request_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.43 User Passkey Request Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('status',  STATUS_SPEC),
+    ('bd_addr', Address.parse_address)
+])
+class HCI_Simple_Pairing_Complete_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.45 Simple Pairing Complete Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('connection_handle',        2),
+    ('link_supervision_timeout', 2)
+])
+class HCI_Link_Supervision_Timeout_Changed_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.46 Link Supervision Timeout Changed Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event([
+    ('bd_addr',                 Address.parse_address),
+    ('host_supported_features', 8)
+])
+class HCI_Remote_Host_Supported_Features_Notification_Event(HCI_Event):
+    '''
+    See Bluetooth spec @ 7.7.50 Remote Host Supported Features Notification Event
+    '''
+
+
+# -----------------------------------------------------------------------------
+class HCI_AclDataPacket(HCI_Packet):
+    '''
+    See Bluetooth spec @ 5.4.2 HCI ACL Data Packets
+    '''
+    hci_packet_type = HCI_ACL_DATA_PACKET
+
+    @staticmethod
+    def from_bytes(packet):
+        # Read the header
+        h, data_total_length = struct.unpack_from('<HH', packet, 1)
+        connection_handle = h & 0xFFF
+        pb_flag = (h >> 12) & 3
+        bc_flag = (h >> 14) & 3
+        data = packet[5:]
+        if len(data) != data_total_length:
+            raise ValueError('invalid packet length')
+        return HCI_AclDataPacket(connection_handle, pb_flag, bc_flag, data_total_length, data)
+
+    def to_bytes(self):
+        h = (self.pb_flag << 12) | (self.bc_flag << 14) | self.connection_handle
+        return struct.pack('<BHH', HCI_ACL_DATA_PACKET, h, self.data_total_length) + self.data
+
+    def __init__(self, connection_handle, pb_flag, bc_flag, data_total_length, data):
+        self.connection_handle = connection_handle
+        self.pb_flag = pb_flag
+        self.bc_flag = bc_flag
+        self.data_total_length = data_total_length
+        self.data = data
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        return f'{color("ACL", "blue")}: handle=0x{self.connection_handle:04x}, pb={self.pb_flag}, bc={self.bc_flag}, data_total_length={self.data_total_length}, data={self.data.hex()}'
+
+
+# -----------------------------------------------------------------------------
+class HCI_AclDataPacketAssembler:
+    def __init__(self, callback):
+        self.callback         = callback
+        self.current_data     = None
+        self.l2cap_pdu_length = 0
+
+    def feed_packet(self, packet):
+        if packet.pb_flag == HCI_ACL_PB_FIRST_NON_FLUSHABLE or packet.pb_flag == HCI_ACL_PB_FIRST_FLUSHABLE:
+            (l2cap_pdu_length,)   = struct.unpack_from('<H', packet.data, 0)
+            self.current_data     = packet.data
+            self.l2cap_pdu_length = l2cap_pdu_length
+        elif packet.pb_flag == HCI_ACL_PB_CONTINUATION:
+            if self.current_data is None:
+                logger.warning('!!! ACL continuation without start')
+                return
+            self.current_data += packet.data
+
+        if len(self.current_data) == self.l2cap_pdu_length + 4:
+            # The packet is complete, invoke the callback
+            logger.debug(f'<<< ACL PDU: {self.current_data.hex()}')
+            self.callback(self.current_data)
+
+            # Reset
+            self.current_data     = None
+            self.l2cap_pdu_length = 0
+        else:
+            # Sanity check
+            if len(self.current_data) > self.l2cap_pdu_length + 4:
+                logger.warning('!!! ACL data exceeds L2CAP PDU')
+                self.current_data     = None
+                self.l2cap_pdu_length = 0
diff --git a/bumble/helpers.py b/bumble/helpers.py
new file mode 100644
index 0000000..09b86ef
--- /dev/null
+++ b/bumble/helpers.py
@@ -0,0 +1,179 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+from colors import color
+
+from .core import name_or_number
+from .gatt import ATT_PDU, ATT_CID
+from .l2cap import (
+    L2CAP_PDU,
+    L2CAP_CONNECTION_REQUEST,
+    L2CAP_CONNECTION_RESPONSE,
+    L2CAP_SIGNALING_CID,
+    L2CAP_LE_SIGNALING_CID,
+    L2CAP_Control_Frame,
+    L2CAP_Connection_Response
+)
+from .hci import (
+    HCI_EVENT_PACKET,
+    HCI_ACL_DATA_PACKET,
+    HCI_DISCONNECTION_COMPLETE_EVENT,
+    HCI_AclDataPacketAssembler
+)
+from .rfcomm import RFCOMM_Frame, RFCOMM_PSM
+from .sdp import SDP_PDU, SDP_PSM
+from .avdtp import (
+    MessageAssembler as AVDTP_MessageAssembler,
+    AVDTP_PSM
+)
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+PSM_NAMES = {
+    RFCOMM_PSM: 'RFCOMM',
+    SDP_PSM:    'SDP',
+    AVDTP_PSM:  'AVDTP'
+    # TODO: add more PSM values
+}
+
+
+# -----------------------------------------------------------------------------
+class PacketTracer:
+    class AclStream:
+        def __init__(self, analyzer):
+            self.analyzer         = analyzer
+            self.packet_assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu)
+            self.avdtp_assemblers = {}    # AVDTP assemblers, by source_cid
+            self.psms             = {}    # PSM, by source_cid
+            self.peer             = None  # ACL stream in the other direction
+
+        def on_acl_pdu(self, pdu):
+            l2cap_pdu = L2CAP_PDU.from_bytes(pdu)
+
+            if l2cap_pdu.cid == ATT_CID:
+                att_pdu = ATT_PDU.from_bytes(l2cap_pdu.payload)
+                self.analyzer.emit(att_pdu)
+            elif l2cap_pdu.cid == L2CAP_SIGNALING_CID or l2cap_pdu.cid == L2CAP_LE_SIGNALING_CID:
+                control_frame = L2CAP_Control_Frame.from_bytes(l2cap_pdu.payload)
+                self.analyzer.emit(control_frame)
+
+                # Check if this signals a new channel
+                if control_frame.code == L2CAP_CONNECTION_REQUEST:
+                    self.psms[control_frame.source_cid] = control_frame.psm
+                elif control_frame.code == L2CAP_CONNECTION_RESPONSE:
+                    if control_frame.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL:
+                        if self.peer:
+                            if psm := self.peer.psms.get(control_frame.source_cid):
+                                # Found a pending connection
+                                self.psms[control_frame.destination_cid] = psm
+
+                                # For AVDTP connections, create a packet assembler for each direction
+                                if psm == AVDTP_PSM:
+                                    self.avdtp_assemblers[control_frame.source_cid] = AVDTP_MessageAssembler(self.on_avdtp_message)
+                                    self.peer.avdtp_assemblers[control_frame.destination_cid] = AVDTP_MessageAssembler(self.peer.on_avdtp_message)
+
+            else:
+                # Try to find the PSM associated with this PDU
+                if self.peer and (psm := self.peer.psms.get(l2cap_pdu.cid)):
+                    if psm == SDP_PSM:
+                        sdp_pdu = SDP_PDU.from_bytes(l2cap_pdu.payload)
+                        self.analyzer.emit(sdp_pdu)
+                    elif psm == RFCOMM_PSM:
+                        rfcomm_frame = RFCOMM_Frame.from_bytes(l2cap_pdu.payload)
+                        self.analyzer.emit(rfcomm_frame)
+                    elif psm == AVDTP_PSM:
+                        self.analyzer.emit(f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, PSM=AVDTP]: {l2cap_pdu.payload.hex()}')
+                        assembler = self.avdtp_assemblers.get(l2cap_pdu.cid)
+                        if assembler:
+                            assembler.on_pdu(l2cap_pdu.payload)
+                    else:
+                        psm_string = name_or_number(PSM_NAMES, psm)
+                        self.analyzer.emit(f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, PSM={psm_string}]: {l2cap_pdu.payload.hex()}')
+                else:
+                    self.analyzer.emit(l2cap_pdu)
+
+        def on_avdtp_message(self, transaction_label, message):
+            self.analyzer.emit(f'{color("AVDTP", "green")} [{transaction_label}] {message}')
+
+        def feed_packet(self, packet):
+            self.packet_assembler.feed_packet(packet)
+
+    class Analyzer:
+        def __init__(self, label, emit_message):
+            self.label        = label
+            self.emit_message = emit_message
+            self.acl_streams  = {}    # ACL streams, by connection handle
+            self.peer         = None  # Analyzer in the other direction
+
+        def start_acl_stream(self, connection_handle):
+            logger.info(f'[{self.label}] +++ Creating ACL stream for connection 0x{connection_handle:04X}')
+            stream = PacketTracer.AclStream(self)
+            self.acl_streams[connection_handle] = stream
+
+            # Associate with a peer stream if we can
+            if peer_stream := self.peer.acl_streams.get(connection_handle):
+                stream.peer = peer_stream
+                peer_stream.peer = stream
+
+            return stream
+
+        def end_acl_stream(self, connection_handle):
+            if connection_handle in self.acl_streams:
+                logger.info(f'[{self.label}] --- Removing ACL stream for connection 0x{connection_handle:04X}')
+                del self.acl_streams[connection_handle]
+
+                # Let the other forwarder know so it can cleanup its stream as well
+                self.peer.end_acl_stream(connection_handle)
+
+        def on_packet(self, packet):
+            self.emit(packet)
+
+            if packet.hci_packet_type == HCI_ACL_DATA_PACKET:
+                # Look for an existing stream for this handle, create one if it is the
+                # first ACL packet for that connection handle
+                if (stream := self.acl_streams.get(packet.connection_handle)) is None:
+                    stream = self.start_acl_stream(packet.connection_handle)
+                stream.feed_packet(packet)
+            elif packet.hci_packet_type == HCI_EVENT_PACKET:
+                if packet.event_code == HCI_DISCONNECTION_COMPLETE_EVENT:
+                    self.end_acl_stream(packet.connection_handle)
+
+        def emit(self, message):
+            self.emit_message(f'[{self.label}] {message}')
+
+    def trace(self, packet, direction=0):
+        if direction == 0:
+            self.host_to_controller_analyzer.on_packet(packet)
+        else:
+            self.controller_to_host_analyzer.on_packet(packet)
+
+    def __init__(
+        self,
+        host_to_controller_label=color('HOST->CONTROLLER', 'blue'),
+        controller_to_host_label=color('CONTROLLER->HOST', 'cyan'),
+        emit_message=logger.info
+    ):
+        self.host_to_controller_analyzer = PacketTracer.Analyzer(host_to_controller_label, emit_message)
+        self.controller_to_host_analyzer = PacketTracer.Analyzer(controller_to_host_label, emit_message)
+        self.host_to_controller_analyzer.peer = self.controller_to_host_analyzer
+        self.controller_to_host_analyzer.peer = self.host_to_controller_analyzer
diff --git a/bumble/hfp.py b/bumble/hfp.py
new file mode 100644
index 0000000..6eeb0d9
--- /dev/null
+++ b/bumble/hfp.py
@@ -0,0 +1,94 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import collections
+from colors import color
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Protocol Support
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+class HfpProtocol:
+    def __init__(self, dlc):
+        self.dlc             = dlc
+        self.buffer          = ''
+        self.lines           = collections.deque()
+        self.lines_available = asyncio.Event()
+
+        dlc.sink = self.feed
+
+    def feed(self, data):
+        # Convert the data to a string if needed
+        if type(data) == bytes:
+            data = data.decode('utf-8')
+
+        logger.debug(f'<<< Data received: {data}')
+
+        # Add to the buffer and look for lines
+        self.buffer += data
+        while (separator := self.buffer.find('\r')) >= 0:
+            line = self.buffer[:separator].strip()
+            self.buffer = self.buffer[separator + 1:]
+            if len(line) > 0:
+                self.on_line(line)
+
+    def on_line(self, line):
+        self.lines.append(line)
+        self.lines_available.set()
+
+    def send_command_line(self, line):
+        logger.debug(color(f'>>> {line}', 'yellow'))
+        self.dlc.write(line + '\r')
+
+    def send_response_line(self, line):
+        logger.debug(color(f'>>> {line}', 'yellow'))
+        self.dlc.write('\r\n' + line + '\r\n')
+
+    async def next_line(self):
+        await self.lines_available.wait()
+        line = self.lines.popleft()
+        if not self.lines:
+            self.lines_available.clear()
+        logger.debug(color(f'<<< {line}', 'green'))
+        return line
+
+    async def initialize_service(self):
+        # Perform Service Level Connection Initialization
+        self.send_command_line('AT+BRSF=2072')  # Retrieve Supported Features
+        line = await(self.next_line())
+        line = await(self.next_line())
+
+        self.send_command_line('AT+CIND=?')
+        line = await(self.next_line())
+        line = await(self.next_line())
+
+        self.send_command_line('AT+CIND?')
+        line = await(self.next_line())
+        line = await(self.next_line())
+
+        self.send_command_line('AT+CMER=3,0,0,1')
+        line = await(self.next_line())
diff --git a/bumble/host.py b/bumble/host.py
new file mode 100644
index 0000000..4057402
--- /dev/null
+++ b/bumble/host.py
@@ -0,0 +1,601 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+from pyee import EventEmitter
+from colors import color
+
+from .hci import *
+from .l2cap import *
+from .att import *
+from .gatt import *
+from .smp import *
+from .core import ConnectionParameters
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH = 27
+HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS     = 1
+HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH    = 27
+HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS        = 1
+
+
+# -----------------------------------------------------------------------------
+class Connection:
+    def __init__(self, host, handle, role, peer_address):
+        self.host                  = host
+        self.handle                = handle
+        self.role                  = role
+        self.peer_address          = peer_address
+        self.assembler             = HCI_AclDataPacketAssembler(self.on_acl_pdu)
+
+    def on_hci_acl_data_packet(self, packet):
+        self.assembler.feed_packet(packet)
+
+    def on_acl_pdu(self, pdu):
+        l2cap_pdu = L2CAP_PDU.from_bytes(pdu)
+
+        if l2cap_pdu.cid == ATT_CID:
+            self.host.on_gatt_pdu(self, l2cap_pdu.payload)
+        elif l2cap_pdu.cid == SMP_CID:
+            self.host.on_smp_pdu(self, l2cap_pdu.payload)
+        else:
+            self.host.on_l2cap_pdu(self, l2cap_pdu.cid, l2cap_pdu.payload)
+
+
+# -----------------------------------------------------------------------------
+class Host(EventEmitter):
+    def __init__(self, controller_source = None, controller_sink = None):
+        super().__init__()
+
+        self.hci_sink                         = None
+        self.ready                            = False  # True when we can accept incoming packets
+        self.connections                      = {}     # Connections, by connection handle
+        self.pending_command                  = None
+        self.pending_response                 = None
+        self.hc_le_acl_data_packet_length     = HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH
+        self.hc_total_num_le_acl_data_packets = HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS
+        self.hc_acl_data_packet_length        = HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH
+        self.hc_total_num_acl_data_packets    = HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS
+        self.acl_packet_queue                 = collections.deque()
+        self.acl_packets_in_flight            = 0
+        self.local_supported_commands         = bytes(64)
+        self.command_semaphore                = asyncio.Semaphore(1)
+        self.long_term_key_provider           = None
+        self.link_key_provider                = None
+        self.pairing_io_capability_provider   = None  # Classic only
+
+        # Connect to the source and sink if specified
+        if controller_source:
+            controller_source.set_packet_sink(self)
+        if controller_sink:
+            self.set_packet_sink(controller_sink)
+
+    async def reset(self):
+        await self.send_command(HCI_Reset_Command())
+        self.ready = True
+
+        response = await self.send_command(HCI_Read_Local_Supported_Commands_Command())
+        if response.return_parameters.status != HCI_SUCCESS:
+            raise ProtocolError(response.return_parameters.status, 'hci')
+        self.local_supported_commands = response.return_parameters.supported_commands
+
+        await self.send_command(HCI_Set_Event_Mask_Command(event_mask = bytes.fromhex('FFFFFFFFFFFFFFFF')))
+        await self.send_command(HCI_LE_Set_Event_Mask_Command(le_event_mask = bytes.fromhex('FFFFF00000000000')))
+        await self.send_command(HCI_Read_Local_Version_Information_Command())
+        await self.send_command(HCI_Write_LE_Host_Support_Command(le_supported_host = 1, simultaneous_le_host = 0))
+
+        response = await self.send_command(HCI_LE_Read_Buffer_Size_Command())
+        if response.return_parameters.status == HCI_SUCCESS:
+            self.hc_le_acl_data_packet_length = response.return_parameters.hc_le_acl_data_packet_length
+            self.hc_total_num_le_acl_data_packets = response.return_parameters.hc_total_num_le_acl_data_packets
+            logger.debug(f'HCI LE ACL flow control: hc_le_acl_data_packet_length={response.return_parameters.hc_le_acl_data_packet_length}, hc_total_num_le_acl_data_packets={response.return_parameters.hc_total_num_le_acl_data_packets}')
+        else:
+            logger.warn(f'HCI_LE_Read_Buffer_Size_Command failed: {response.return_parameters.status}')
+        if response.return_parameters.hc_le_acl_data_packet_length == 0 or response.return_parameters.hc_total_num_le_acl_data_packets == 0:
+            # Read the non-LE-specific values
+            response = await self.send_command(HCI_Read_Buffer_Size_Command())
+            if response.return_parameters.status == HCI_SUCCESS:
+                self.hc_acl_data_packet_length        = response.return_parameters.hc_le_acl_data_packet_length
+                self.hc_le_acl_data_packet_length     = self.hc_le_acl_data_packet_length or self.hc_acl_data_packet_length
+                self.hc_total_num_acl_data_packets    = response.return_parameters.hc_total_num_le_acl_data_packets
+                self.hc_total_num_le_acl_data_packets = self.hc_total_num_le_acl_data_packets or self.hc_total_num_acl_data_packets
+                logger.debug(f'HCI LE ACL flow control: hc_le_acl_data_packet_length={self.hc_le_acl_data_packet_length}, hc_total_num_le_acl_data_packets={self.hc_total_num_le_acl_data_packets}')
+            else:
+                logger.warn(f'HCI_Read_Buffer_Size_Command failed: {response.return_parameters.status}')
+
+        self.reset_done = True
+
+    @property
+    def controller(self):
+        return self.hci_sink
+
+    @controller.setter
+    def controller(self, controller):
+        self.set_packet_sink(controller)
+        if controller:
+            controller.set_packet_sink(self)
+
+    def set_packet_sink(self, sink):
+        self.hci_sink = sink
+
+    def send_hci_packet(self, packet):
+        self.hci_sink.on_packet(packet.to_bytes())
+
+    async def send_command(self, command):
+        logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: {command}')
+
+        # Wait until we can send (only one pending command at a time)
+        async with self.command_semaphore:
+            assert(self.pending_command is None)
+            assert(self.pending_response is None)
+
+            # Create a future value to hold the eventual response
+            self.pending_response = asyncio.get_running_loop().create_future()
+            self.pending_command = command
+
+            try:
+                self.send_hci_packet(command)
+                response = await self.pending_response
+                # TODO: check error values
+                return response
+            except Exception as error:
+                logger.warning(f'{color("!!! Exception while sending HCI packet:", "red")} {error}')
+                # raise error
+            finally:
+                self.pending_command = None
+                self.pending_response = None
+
+    # Use this method to send a command from a task
+    def send_command_sync(self, command):
+        async def send_command(command):
+            await self.send_command(command)
+
+        asyncio.create_task(send_command(command))
+
+    def send_l2cap_pdu(self, connection_handle, cid, pdu):
+        l2cap_pdu = L2CAP_PDU(cid, pdu).to_bytes()
+
+        # Send the data to the controller via ACL packets
+        bytes_remaining = len(l2cap_pdu)
+        offset = 0
+        pb_flag = 0
+        while bytes_remaining:
+            data_total_length = min(bytes_remaining, self.hc_le_acl_data_packet_length)
+            acl_packet = HCI_AclDataPacket(
+                connection_handle  = connection_handle,
+                pb_flag            = pb_flag,
+                bc_flag            = 0,
+                data_total_length  = data_total_length,
+                data               = l2cap_pdu[offset:offset + data_total_length]
+            )
+            logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: (CID={cid}) {acl_packet}')
+            self.queue_acl_packet(acl_packet)
+            pb_flag = 1
+            offset += data_total_length
+            bytes_remaining -= data_total_length
+
+    def queue_acl_packet(self, acl_packet):
+        self.acl_packet_queue.appendleft(acl_packet)
+        self.check_acl_packet_queue()
+
+        if len(self.acl_packet_queue):
+            logger.debug(f'{self.acl_packets_in_flight} ACL packets in flight, {len(self.acl_packet_queue)} in queue')
+
+    def check_acl_packet_queue(self):
+        # Send all we can
+        while len(self.acl_packet_queue) > 0 and self.acl_packets_in_flight < self.hc_total_num_le_acl_data_packets:
+            packet = self.acl_packet_queue.pop()
+            self.send_hci_packet(packet)
+            self.acl_packets_in_flight += 1
+
+    # Packet Sink protocol (packets coming from the controller via HCI)
+    def on_packet(self, packet):
+        hci_packet = HCI_Packet.from_bytes(packet)
+        if self.ready or (
+            hci_packet.hci_packet_type == HCI_EVENT_PACKET and
+            hci_packet.event_code == HCI_COMMAND_COMPLETE_EVENT and
+            hci_packet.command_opcode == HCI_RESET_COMMAND
+        ):
+            self.on_hci_packet(hci_packet)
+        else:
+            logger.debug('reset not done, ignoring packet from controller')
+
+    def on_hci_packet(self, packet):
+        logger.debug(f'{color("### CONTROLLER -> HOST", "green")}: {packet}')
+
+        # If the packet is a command, invoke the handler for this packet
+        if packet.hci_packet_type == HCI_COMMAND_PACKET:
+            self.on_hci_command_packet(packet)
+        elif packet.hci_packet_type == HCI_EVENT_PACKET:
+            self.on_hci_event_packet(packet)
+        elif packet.hci_packet_type == HCI_ACL_DATA_PACKET:
+            self.on_hci_acl_data_packet(packet)
+        else:
+            logger.warning(f'!!! unknown packet type {packet.hci_packet_type}')
+
+    def on_hci_command_packet(self, command):
+        logger.warning(f'!!! unexpected command packet: {command}')
+
+    def on_hci_event_packet(self, event):
+        handler_name = f'on_{event.name.lower()}'
+        handler = getattr(self, handler_name, self.on_hci_event)
+        handler(event)
+
+    def on_hci_acl_data_packet(self, packet):
+        # Look for the connection to which this data belongs
+        if connection := self.connections.get(packet.connection_handle):
+            connection.on_hci_acl_data_packet(packet)
+
+    def on_gatt_pdu(self, connection, pdu):
+        self.emit('gatt_pdu', connection.handle, pdu)
+
+    def on_smp_pdu(self, connection, pdu):
+        self.emit('smp_pdu', connection.handle, pdu)
+
+    def on_l2cap_pdu(self, connection, cid, pdu):
+        self.emit('l2cap_pdu', connection.handle, cid, pdu)
+
+    def on_command_processed(self, event):
+        if self.pending_response:
+            # Check that it is what we were expecting
+            if self.pending_command.op_code != event.command_opcode:
+                logger.warning(f'!!! command result mismatch, expected 0x{self.pending_command.op_code:X} but got 0x{event.command_opcode:X}')
+
+            self.pending_response.set_result(event)
+        else:
+            logger.warning('!!! no pending response future to set')
+
+    ############################################################
+    # HCI handlers
+    ############################################################
+    def on_hci_event(self, event):
+        logger.warning(f'{color(f"--- Ignoring event {event}", "red")}')
+
+    def on_hci_command_complete_event(self, event):
+        if event.command_opcode == 0:
+            # This is used just for the Num_HCI_Command_Packets field, not related to an actual command
+            logger.debug('no-command event')
+        else:
+            return self.on_command_processed(event)
+
+    def on_hci_command_status_event(self, event):
+        return self.on_command_processed(event)
+
+    def on_hci_number_of_completed_packets_event(self, event):
+        total_packets = sum(event.num_completed_packets)
+        if total_packets <= self.acl_packets_in_flight:
+            self.acl_packets_in_flight -= total_packets
+            self.check_acl_packet_queue()
+        else:
+            logger.warning(color(f'!!! {total_packets} completed but only {self.acl_packets_in_flight} in flight'))
+            self.acl_packets_in_flight = 0
+
+    # Classic only
+    def on_hci_connection_request_event(self, event):
+        # For now, just accept everything
+        # TODO: delegate the decision
+        self.send_command_sync(
+            HCI_Accept_Connection_Request_Command(
+                bd_addr = event.bd_addr,
+                role    = 0x01  # Remain the peripheral
+            )
+        )
+
+    def on_hci_le_connection_complete_event(self, event):
+        # Check if this is a cancellation
+        if event.status == HCI_SUCCESS:
+            # Create/update the connection
+            logger.debug(f'### CONNECTION: [0x{event.connection_handle:04X}] {event.peer_address} as {HCI_Constant.role_name(event.role)}')
+
+            connection = self.connections.get(event.connection_handle)
+            if connection is None:
+                connection = Connection(self, event.connection_handle, event.role, event.peer_address)
+                self.connections[event.connection_handle] = connection
+
+            # Notify the client
+            connection_parameters = ConnectionParameters(
+                event.conn_interval,
+                event.conn_latency,
+                event.supervision_timeout
+            )
+            self.emit(
+                'connection',
+                event.connection_handle,
+                BT_LE_TRANSPORT,
+                event.peer_address,
+                None,
+                event.role,
+                connection_parameters
+            )
+        else:
+            logger.debug(f'### CONNECTION FAILED: {event.status}')
+
+            # Notify the listeners
+            self.emit('connection_failure', event.status)
+
+    def on_hci_le_enhanced_connection_complete_event(self, event):
+        # Just use the same implementation as for the non-enhanced event for now
+        self.on_hci_le_connection_complete_event(event)
+
+    def on_hci_connection_complete_event(self, event):
+        if event.status == HCI_SUCCESS:
+            # Create/update the connection
+            logger.debug(f'### BR/EDR CONNECTION: [0x{event.connection_handle:04X}] {event.bd_addr}')
+
+            connection = self.connections.get(event.connection_handle)
+            if connection is None:
+                connection = Connection(self, event.connection_handle, BT_CENTRAL_ROLE, event.bd_addr)
+                self.connections[event.connection_handle] = connection
+
+            # Notify the client
+            self.emit(
+                'connection',
+                event.connection_handle,
+                BT_BR_EDR_TRANSPORT,
+                event.bd_addr,
+                None,
+                BT_CENTRAL_ROLE,
+                None
+            )
+        else:
+            logger.debug(f'### BR/EDR CONNECTION FAILED: {event.status}')
+
+            # Notify the client
+            self.emit('connection_failure', event.connection_handle, event.status)
+
+    def on_hci_disconnection_complete_event(self, event):
+        # Find the connection
+        if (connection := self.connections.get(event.connection_handle)) is None:
+            logger.warning('!!! DISCONNECTION COMPLETE: unknown handle')
+            return
+
+        if event.status == HCI_SUCCESS:
+            logger.debug(f'### DISCONNECTION: [0x{event.connection_handle:04X}] {connection.peer_address} as {HCI_Constant.role_name(connection.role)}, reason={event.reason}')
+            del self.connections[event.connection_handle]
+
+            # Notify the listeners
+            self.emit('disconnection', event.connection_handle, event.reason)
+        else:
+            logger.debug(f'### DISCONNECTION FAILED: {event.status}')
+
+            # Notify the listeners
+            self.emit('disconnection_failure', event.status)
+
+    def on_hci_le_connection_update_complete_event(self, event):
+        if (connection := self.connections.get(event.connection_handle)) is None:
+            logger.warning('!!! CONNECTION PARAMETERS UPDATE COMPLETE: unknown handle')
+            return
+
+        # Notify the client
+        if event.status == HCI_SUCCESS:
+            connection_parameters = ConnectionParameters(
+                event.conn_interval,
+                event.conn_latency,
+                event.supervision_timeout
+            )
+            self.emit('connection_parameters_update', connection.handle, connection_parameters)
+        else:
+            self.emit('connection_parameters_update_failure', connection.handle, event.status)
+
+    def on_hci_le_phy_update_complete_event(self, event):
+        if (connection := self.connections.get(event.connection_handle)) is None:
+            logger.warning('!!! CONNECTION PHY UPDATE COMPLETE: unknown handle')
+            return
+
+        # Notify the client
+        if event.status == HCI_SUCCESS:
+            connection_phy = ConnectionPHY(event.tx_phy, event.rx_phy)
+            self.emit('connection_phy_update', connection.handle, connection_phy)
+        else:
+            self.emit('connection_phy_update_failure', connection.handle, event.status)
+
+    def on_hci_le_advertising_report_event(self, event):
+        for report in event.reports:
+            self.emit(
+                'advertising_report',
+                report.address,
+                report.data,
+                report.rssi,
+                report.event_type
+            )
+
+    def on_hci_le_remote_connection_parameter_request_event(self, event):
+        if event.connection_handle not in self.connections:
+            logger.warning('!!! REMOTE CONNECTION PARAMETER REQUEST: unknown handle')
+            return
+
+        # For now, just accept everything
+        # TODO: delegate the decision
+        self.send_command_sync(
+            HCI_LE_Remote_Connection_Parameter_Request_Reply_Command(
+                connection_handle = event.connection_handle,
+                interval_min      = event.interval_min,
+                interval_max      = event.interval_max,
+                latency           = event.latency,
+                timeout           = event.timeout,
+                minimum_ce_length = 0,
+                maximum_ce_length = 0
+            )
+        )
+
+    def on_hci_le_long_term_key_request_event(self, event):
+        if (connection := self.connections.get(event.connection_handle)) is None:
+            logger.warning('!!! LE LONG TERM KEY REQUEST: unknown handle')
+            return
+
+        async def send_long_term_key():
+            if self.long_term_key_provider is None:
+                logger.debug('no long term key provider')
+                long_term_key = None
+            else:
+                long_term_key = await self.long_term_key_provider(
+                    connection.handle,
+                    event.random_number,
+                    event.encryption_diversifier
+                )
+            if long_term_key:
+                response = HCI_LE_Long_Term_Key_Request_Reply_Command(
+                    connection_handle = event.connection_handle,
+                    long_term_key     = long_term_key
+                )
+            else:
+                response = HCI_LE_Long_Term_Key_Request_Negative_Reply_Command(
+                    connection_handle = event.connection_handle
+                )
+
+            await self.send_command(response)
+
+        asyncio.create_task(send_long_term_key())
+
+    def on_hci_synchronous_connection_complete_event(self, event):
+        pass
+
+    def on_hci_synchronous_connection_changed_event(self, event):
+        pass
+
+    def on_hci_role_change_event(self, event):
+        if event.status == HCI_SUCCESS:
+            logger.debug(f'role change for {event.bd_addr}: {HCI_Constant.role_name(event.new_role)}')
+            # TODO: lookup the connection and update the role
+        else:
+            logger.debug(f'role change for {event.bd_addr} failed: {HCI_Constant.error_name(event.status)}')
+
+    def on_hci_le_data_length_change_event(self, event):
+        self.emit(
+            'connection_data_length_change',
+            event.connection_handle,
+            event.max_tx_octets,
+            event.max_tx_time,
+            event.max_rx_octets,
+            event.max_rx_time
+        )
+
+    def on_hci_authentication_complete_event(self, event):
+        # Notify the client
+        if event.status == HCI_SUCCESS:
+            self.emit('connection_authentication', event.connection_handle)
+        else:
+            self.emit('connection_authentication_failure', event.connection_handle, event.status)
+
+    def on_hci_encryption_change_event(self, event):
+        # Notify the client
+        if event.status == HCI_SUCCESS:
+            self.emit('connection_encryption_change', event.connection_handle, event.encryption_enabled)
+        else:
+            self.emit('connection_encryption_failure', event.connection_handle, event.status)
+
+    def on_hci_encryption_key_refresh_complete_event(self, event):
+        # Notify the client
+        if event.status == HCI_SUCCESS:
+            self.emit('connection_encryption_key_refresh', event.connection_handle)
+        else:
+            self.emit('connection_encryption_key_refresh_failure', event.connection_handle, event.status)
+
+    def on_hci_link_supervision_timeout_changed_event(self, event):
+        pass
+
+    def on_hci_max_slots_change_event(self, event):
+        pass
+
+    def on_hci_page_scan_repetition_mode_change_event(self, event):
+        pass
+
+    def on_hci_link_key_notification_event(self, event):
+        logger.debug(f'link key for {event.bd_addr}: {event.link_key.hex()}, type={HCI_Constant.link_key_type_name(event.key_type)}')
+        self.emit('link_key', event.bd_addr, event.link_key, event.key_type)
+
+    def on_hci_simple_pairing_complete_event(self, event):
+        logger.debug(f'simple pairing complete for {event.bd_addr}: status={HCI_Constant.status_name(event.status)}')
+
+    def on_hci_pin_code_request_event(self, event):
+        # For now, just refuse all requests
+        # TODO: delegate the decision
+        self.send_command_sync(
+            HCI_PIN_Code_Request_Negative_Reply_Command(
+                bd_addr = event.bd_addr
+            )
+        )
+
+    def on_hci_link_key_request_event(self, event):
+        async def send_link_key():
+            if self.link_key_provider is None:
+                logger.debug('no link key provider')
+                link_key = None
+            else:
+                link_key = await self.link_key_provider(event.bd_addr)
+            if link_key:
+                response = HCI_Link_Key_Request_Reply_Command(
+                    bd_addr  = event.bd_addr,
+                    link_key = link_key
+                )
+            else:
+                response = HCI_Link_Key_Request_Negative_Reply_Command(
+                    bd_addr = event.bd_addr
+                )
+
+            await self.send_command(response)
+
+        asyncio.create_task(send_link_key())
+
+    def on_hci_io_capability_request_event(self, event):
+        self.emit('authentication_io_capability_request', event.bd_addr)
+
+    def on_hci_io_capability_response_event(self, event):
+        pass
+
+    def on_hci_user_confirmation_request_event(self, event):
+        self.emit('authentication_user_confirmation_request', event.bd_addr, event.numeric_value)
+
+    def on_hci_user_passkey_request_event(self, event):
+        self.emit('authentication_user_passkey_request', event.bd_addr)
+
+    def on_hci_inquiry_complete_event(self, event):
+        self.emit('inquiry_complete')
+
+    def on_hci_inquiry_result_with_rssi_event(self, event):
+        for response in event.responses:
+            self.emit(
+                'inquiry_result',
+                response.bd_addr,
+                response.class_of_device,
+                b'',
+                response.rssi
+            )
+
+    def on_hci_extended_inquiry_result_event(self, event):
+        self.emit(
+            'inquiry_result',
+            event.bd_addr,
+            event.class_of_device,
+            event.extended_inquiry_response,
+            event.rssi
+        )
+
+    def on_hci_remote_name_request_complete_event(self, event):
+        if event.status != HCI_SUCCESS:
+            self.emit('remote_name_failure', event.bd_addr, event.status)
+        else:
+            self.emit('remote_name', event.bd_addr, event.remote_name)
diff --git a/bumble/keys.py b/bumble/keys.py
new file mode 100644
index 0000000..f51cfe6
--- /dev/null
+++ b/bumble/keys.py
@@ -0,0 +1,273 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Keys and Key Storage
+#
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import os
+import json
+from colors import color
+
+from .hci import Address
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+class PairingKeys:
+    class Key:
+        def __init__(self, value, authenticated=False, ediv=None, rand=None):
+            self.value         = value
+            self.authenticated = authenticated
+            self.ediv          = ediv
+            self.rand          = rand
+
+        @classmethod
+        def from_dict(cls, key_dict):
+            value = bytes.fromhex(key_dict['value'])
+            authenticated = key_dict.get('authenticated', False)
+            ediv = key_dict.get('ediv')
+            rand = key_dict.get('rand')
+            if rand is not None:
+                rand = bytes.fromhex(rand)
+
+            return cls(value, authenticated, ediv, rand)
+
+        def to_dict(self):
+            key_dict = {'value': self.value.hex(), 'authenticated': self.authenticated}
+            if self.ediv is not None:
+                key_dict['ediv'] = self.ediv
+            if self.rand is not None:
+                key_dict['rand'] = self.rand.hex()
+
+            return key_dict
+
+    def __init__(self):
+        self.address_type   = None
+        self.ltk            = None
+        self.ltk_central    = None
+        self.ltk_peripheral = None
+        self.irk            = None
+        self.csrk           = None
+        self.link_key       = None  # Classic
+
+    @staticmethod
+    def key_from_dict(keys_dict, key_name):
+        key_dict = keys_dict.get(key_name)
+        if key_dict is not None:
+            return PairingKeys.Key.from_dict(key_dict)
+
+    @staticmethod
+    def from_dict(keys_dict):
+        keys = PairingKeys()
+
+        keys.address_type   = keys_dict.get('address_type')
+        keys.ltk            = PairingKeys.key_from_dict(keys_dict, 'ltk')
+        keys.ltk_central    = PairingKeys.key_from_dict(keys_dict, 'ltk_central')
+        keys.ltk_peripheral = PairingKeys.key_from_dict(keys_dict, 'ltk_peripheral')
+        keys.irk            = PairingKeys.key_from_dict(keys_dict, 'irk')
+        keys.csrk           = PairingKeys.key_from_dict(keys_dict, 'csrk')
+        keys.link_key       = PairingKeys.key_from_dict(keys_dict, 'link_key')
+
+        return keys
+
+    def to_dict(self):
+        keys = {}
+
+        if self.address_type is not None:
+            keys['address_type'] = self.address_type
+
+        if self.ltk is not None:
+            keys['ltk'] = self.ltk.to_dict()
+
+        if self.ltk_central is not None:
+            keys['ltk_central'] = self.ltk_central.to_dict()
+
+        if self.ltk_peripheral is not None:
+            keys['ltk_peripheral'] = self.ltk_peripheral.to_dict()
+
+        if self.irk is not None:
+            keys['irk'] = self.irk.to_dict()
+
+        if self.csrk is not None:
+            keys['csrk'] = self.csrk.to_dict()
+
+        if self.link_key is not None:
+            keys['link_key'] = self.link_key.to_dict()
+
+        return keys
+
+    def print(self, prefix=''):
+        keys_dict = self.to_dict()
+        for (property, value) in keys_dict.items():
+            if type(value) is dict:
+                print(f'{prefix}{color(property, "cyan")}:')
+                for (key_property, key_value) in value.items():
+                    print(f'{prefix}  {color(key_property, "green")}: {key_value}')
+            else:
+                print(f'{prefix}{color(property, "cyan")}: {value}')
+
+
+# -----------------------------------------------------------------------------
+class KeyStore:
+    async def delete(self, name):
+        pass
+
+    async def update(self, name, keys):
+        pass
+
+    async def get(self, name):
+        return PairingKeys()
+
+    async def get_all(self):
+        return []
+
+    async def get_resolving_keys(self):
+        all_keys = await self.get_all()
+        resolving_keys = []
+        for (name, keys) in all_keys:
+            if keys.irk is not None:
+                if keys.address_type is None:
+                    address_type = Address.RANDOM_DEVICE_ADDRESS
+                else:
+                    address_type = keys.address_type
+                resolving_keys.append((keys.irk.value, Address(name, address_type)))
+
+        return resolving_keys
+
+    async def print(self, prefix=''):
+        entries = await self.get_all()
+        separator = ''
+        for (name, keys) in entries:
+            print(separator + prefix + color(name, 'yellow'))
+            keys.print(prefix = prefix + '  ')
+            separator = '\n'
+
+    @staticmethod
+    def create_for_device(device_config):
+        if device_config.keystore is None:
+            return None
+
+        keystore_type = device_config.keystore.split(':', 1)[0]
+        if keystore_type == 'JsonKeyStore':
+            return JsonKeyStore.from_device_config(device_config)
+
+        return None
+
+
+# -----------------------------------------------------------------------------
+class JsonKeyStore(KeyStore):
+    APP_NAME          = 'Bumble'
+    APP_AUTHOR        = 'Google'
+    KEYS_DIR          = 'Pairing'
+    DEFAULT_NAMESPACE = '__DEFAULT__'
+
+    def __init__(self, namespace, filename=None):
+        self.namespace = namespace if namespace is not None else self.DEFAULT_NAMESPACE
+
+        if filename is None:
+            # Use a default for the current user
+            import appdirs
+            self.directory_name = os.path.join(
+                appdirs.user_data_dir(self.APP_NAME, self.APP_AUTHOR),
+                self.KEYS_DIR
+            )
+            json_filename = f'{self.namespace}.json'.lower().replace(':', '-')
+            self.filename = os.path.join(self.directory_name, json_filename)
+        else:
+            self.filename = filename
+            self.directory_name = os.path.dirname(os.path.abspath(self.filename))
+
+        logger.debug(f'JSON keystore: {self.filename}')
+
+    @staticmethod
+    def from_device_config(device_config):
+        params = device_config.keystore.split(':', 1)[1:]
+        namespace = str(device_config.address)
+        if params:
+            filename = params[1]
+        else:
+            filename = None
+
+        return JsonKeyStore(namespace, filename)
+
+    async def load(self):
+        try:
+            with open(self.filename, 'r') as json_file:
+                return json.load(json_file)
+        except FileNotFoundError:
+            return {}
+
+    async def save(self, db):
+        # Create the directory if it doesn't exist
+        if not os.path.exists(self.directory_name):
+            os.makedirs(self.directory_name, exist_ok=True)
+
+        # Save to a temporary file
+        temp_filename = self.filename + '.tmp'
+        with open(temp_filename, 'w') as output:
+            json.dump(db, output, sort_keys=True, indent=4)
+
+        # Atomically replace the previous file
+        os.rename(temp_filename, self.filename)
+
+    async def delete(self, name):
+        db = await self.load()
+
+        namespace = db.get(self.namespace)
+        if namespace is None:
+            raise KeyError(name)
+
+        del namespace[name]
+        await self.save(db)
+
+    async def update(self, name, keys):
+        db = await self.load()
+
+        namespace = db.setdefault(self.namespace, {})
+        namespace[name] = keys.to_dict()
+
+        await self.save(db)
+
+    async def get_all(self):
+        db = await self.load()
+
+        namespace = db.get(self.namespace)
+        if namespace is None:
+            return []
+
+        return [(name, PairingKeys.from_dict(keys)) for (name, keys) in namespace.items()]
+
+    async def get(self, name):
+        db = await self.load()
+
+        namespace = db.get(self.namespace)
+        if namespace is None:
+            return None
+
+        keys = namespace.get(name)
+        if keys is None:
+            return None
+
+        return PairingKeys.from_dict(keys)
diff --git a/bumble/l2cap.py b/bumble/l2cap.py
new file mode 100644
index 0000000..7a2ca2b
--- /dev/null
+++ b/bumble/l2cap.py
@@ -0,0 +1,1097 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import struct
+
+from colors import color
+
+from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError
+from .hci import (HCI_LE_Connection_Update_Command, HCI_Object, key_with_value,
+                  name_or_number)
+from .utils import EventEmitter
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+L2CAP_SIGNALING_CID    = 0x01
+L2CAP_LE_SIGNALING_CID = 0x05
+
+L2CAP_MIN_LE_MTU     = 23
+L2CAP_MIN_BR_EDR_MTU = 48
+
+L2CAP_DEFAULT_MTU = 2048  # Default value for the MTU we are willing to accept
+
+# See Bluetooth spec @ Vol 3, Part A - Table 2.1: CID name space on ACL-U, ASB-U, and AMP-U logical links
+L2CAP_ACL_U_DYNAMIC_CID_RANGE_START = 0x0040
+L2CAP_ACL_U_DYNAMIC_CID_RANGE_END   = 0xFFFF
+
+# See Bluetooth spec @ Vol 3, Part A - Table 2.2: CID name space on LE-U logical link
+L2CAP_LE_U_DYNAMIC_CID_RANGE_START = 0x0040
+L2CAP_LE_U_DYNAMIC_CID_RANGE_START = 0x007F
+
+# Frame types
+L2CAP_COMMAND_REJECT                       = 0x01
+L2CAP_CONNECTION_REQUEST                   = 0x02
+L2CAP_CONNECTION_RESPONSE                  = 0x03
+L2CAP_CONFIGURE_REQUEST                    = 0x04
+L2CAP_CONFIGURE_RESPONSE                   = 0x05
+L2CAP_DISCONNECTION_REQUEST                = 0x06
+L2CAP_DISCONNECTION_RESPONSE               = 0x07
+L2CAP_ECHO_REQUEST                         = 0x08
+L2CAP_ECHO_RESPONSE                        = 0x09
+L2CAP_INFORMATION_REQUEST                  = 0x0A
+L2CAP_INFORMATION_RESPONSE                 = 0x0B
+L2CAP_CREATE_CHANNEL_REQUEST               = 0x0C
+L2CAP_CREATE_CHANNEL_RESPONSE              = 0x0D
+L2CAP_MOVE_CHANNEL_REQUEST                 = 0x0E
+L2CAP_MOVE_CHANNEL_RESPONSE                = 0x0F
+L2CAP_MOVE_CHANNEL_CONFIRMATION            = 0x10
+L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE   = 0x11
+L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST  = 0x12
+L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE = 0x13
+L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST   = 0x14
+L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE  = 0x15
+L2CAP_LE_FLOW_CONTROL_CREDIT               = 0x16
+
+L2CAP_CONTROL_FRAME_NAMES = {
+    L2CAP_COMMAND_REJECT:                       'L2CAP_COMMAND_REJECT',
+    L2CAP_CONNECTION_REQUEST:                   'L2CAP_CONNECTION_REQUEST',
+    L2CAP_CONNECTION_RESPONSE:                  'L2CAP_CONNECTION_RESPONSE',
+    L2CAP_CONFIGURE_REQUEST:                    'L2CAP_CONFIGURE_REQUEST',
+    L2CAP_CONFIGURE_RESPONSE:                   'L2CAP_CONFIGURE_RESPONSE',
+    L2CAP_DISCONNECTION_REQUEST:                'L2CAP_DISCONNECTION_REQUEST',
+    L2CAP_DISCONNECTION_RESPONSE:               'L2CAP_DISCONNECTION_RESPONSE',
+    L2CAP_ECHO_REQUEST:                         'L2CAP_ECHO_REQUEST',
+    L2CAP_ECHO_RESPONSE:                        'L2CAP_ECHO_RESPONSE',
+    L2CAP_INFORMATION_REQUEST:                  'L2CAP_INFORMATION_REQUEST',
+    L2CAP_INFORMATION_RESPONSE:                 'L2CAP_INFORMATION_RESPONSE',
+    L2CAP_CREATE_CHANNEL_REQUEST:               'L2CAP_CREATE_CHANNEL_REQUEST',
+    L2CAP_CREATE_CHANNEL_RESPONSE:              'L2CAP_CREATE_CHANNEL_RESPONSE',
+    L2CAP_MOVE_CHANNEL_REQUEST:                 'L2CAP_MOVE_CHANNEL_REQUEST',
+    L2CAP_MOVE_CHANNEL_RESPONSE:                'L2CAP_MOVE_CHANNEL_RESPONSE',
+    L2CAP_MOVE_CHANNEL_CONFIRMATION:            'L2CAP_MOVE_CHANNEL_CONFIRMATION',
+    L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE:   'L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE',
+    L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST:  'L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST',
+    L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE: 'L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE',
+    L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST:   'L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST',
+    L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE:  'L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE',
+    L2CAP_LE_FLOW_CONTROL_CREDIT:               'L2CAP_LE_FLOW_CONTROL_CREDIT'
+}
+
+L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT = 0x0000
+L2CAP_CONNECTION_PARAMETERS_REJECTED_RESULT = 0x0001
+
+L2CAP_COMMAND_NOT_UNDERSTOOD_REASON = 0x0000
+L2CAP_SIGNALING_MTU_EXCEEDED_REASON = 0x0001
+L2CAP_INVALID_CID_IN_REQUEST_REASON = 0x0002
+
+L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU = 2048
+L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS = 2048
+
+L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE = 0x01
+
+L2CAP_MTU_CONFIGURATION_PARAMETER_TYPE = 0x01
+
+
+# -----------------------------------------------------------------------------
+# Classes
+# -----------------------------------------------------------------------------
+class L2CAP_PDU:
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 3 DATA PACKET FORMAT
+    '''
+
+    @staticmethod
+    def from_bytes(data):
+        # Sanity check
+        if len(data) < 4:
+            raise ValueError('not enough data for L2CAP header')
+
+        _, l2cap_pdu_cid = struct.unpack_from('<HH', data, 0)
+        l2cap_pdu_payload = data[4:]
+
+        return L2CAP_PDU(l2cap_pdu_cid, l2cap_pdu_payload)
+
+    def to_bytes(self):
+        header = struct.pack('<HH', len(self.payload), self.cid)
+        return header + self.payload
+
+    def __init__(self, cid, payload):
+        self.cid     = cid
+        self.payload = payload
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        return f'{color("L2CAP", "green")} [CID={self.cid}]: {self.payload.hex()}'
+
+
+# -----------------------------------------------------------------------------
+class L2CAP_Control_Frame:
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4 SIGNALING PACKET FORMATS
+    '''
+    classes = {}
+    code = 0
+
+    @staticmethod
+    def from_bytes(pdu):
+        code = pdu[0]
+
+        cls = L2CAP_Control_Frame.classes.get(code)
+        if cls is None:
+            instance = L2CAP_Control_Frame(pdu)
+            instance.name = L2CAP_Control_Frame.code_name(code)
+            instance.code = code
+            return instance
+        self = cls.__new__(cls)
+        L2CAP_Control_Frame.__init__(self, pdu)
+        self.identifier = pdu[1]
+        length = struct.unpack_from('<H', pdu, 2)[0]
+        if length + 4 != len(pdu):
+            logger.warn(color(f'!!! length mismatch: expected {len(pdu) - 4} but got {length}', 'red'))
+        if hasattr(self, 'fields'):
+            self.init_from_bytes(pdu, 4)
+        return self
+
+    @staticmethod
+    def code_name(code):
+        return name_or_number(L2CAP_CONTROL_FRAME_NAMES, code)
+
+    @staticmethod
+    def decode_configuration_options(data):
+        options = []
+        while len(data) >= 2:
+            type = data[0]
+            length = data[1]
+            value = data[2:2 + length]
+            data = data[2 + length:]
+            options.append((type, value))
+
+        return options
+
+    @staticmethod
+    def encode_configuration_options(options):
+        return b''.join([bytes([option[0], len(option[1])]) + option[1] for option in options])
+
+    @staticmethod
+    def subclass(fields):
+        def inner(cls):
+            cls.name = cls.__name__.upper()
+            cls.code = key_with_value(L2CAP_CONTROL_FRAME_NAMES, cls.name)
+            if cls.code is None:
+                raise KeyError(f'Control Frame name {cls.name} not found in L2CAP_CONTROL_FRAME_NAMES')
+            cls.fields = fields
+
+            # Register a factory for this class
+            L2CAP_Control_Frame.classes[cls.code] = cls
+
+            return cls
+
+        return inner
+
+    def __init__(self, pdu=None, **kwargs):
+        self.identifier = kwargs.get('identifier', 0)
+        if hasattr(self, 'fields') and kwargs:
+            HCI_Object.init_from_fields(self, self.fields, kwargs)
+        if pdu is None:
+            data = HCI_Object.dict_to_bytes(kwargs, self.fields)
+            pdu = bytes([self.code, self.identifier]) + struct.pack('<H', len(data)) + data
+        self.pdu = pdu
+
+    def init_from_bytes(self, pdu, offset):
+        return HCI_Object.init_from_bytes(self, pdu, offset, self.fields)
+
+    def to_bytes(self):
+        return self.pdu
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        result = f'{color(self.name, "yellow")} [ID={self.identifier}]'
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        else:
+            if len(self.pdu) > 1:
+                result += f': {self.pdu.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('reason', {'size': 2, 'mapper': lambda x: L2CAP_Command_Reject.reason_name(x)}),
+    ('data',   '*')
+])
+class L2CAP_Command_Reject(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.1 COMMAND REJECT
+    '''
+
+    COMMAND_NOT_UNDERSTOOD = 0x0000
+    SIGNALING_MTU_EXCEEDED = 0x0001
+    INVALID_CID_IN_REQUEST = 0x0002
+
+    REASON_NAMES = {
+        COMMAND_NOT_UNDERSTOOD: 'COMMAND_NOT_UNDERSTOOD',
+        SIGNALING_MTU_EXCEEDED: 'SIGNALING_MTU_EXCEEDED',
+        INVALID_CID_IN_REQUEST: 'INVALID_CID_IN_REQUEST'
+    }
+
+    @staticmethod
+    def reason_name(reason):
+        return name_or_number(L2CAP_Command_Reject.REASON_NAMES, reason)
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('psm',        2),
+    ('source_cid', 2)
+])
+class L2CAP_Connection_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.2 CONNECTION REQUEST
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('destination_cid', 2),
+    ('source_cid',      2),
+    ('result',          {'size': 2, 'mapper': lambda x: L2CAP_Connection_Response.result_name(x)}),
+    ('status',          2)
+])
+class L2CAP_Connection_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.3 CONNECTION RESPONSE
+    '''
+
+    CONNECTION_SUCCESSFUL                               = 0x0000
+    CONNECTION_PENDING                                  = 0x0001
+    CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED             = 0x0002
+    CONNECTION_REFUSED_SECURITY_BLOCK                   = 0x0003
+    CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE           = 0x0004
+    CONNECTION_REFUSED_INVALID_SOURCE_CID               = 0x0006
+    CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED     = 0x0007
+    CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS          = 0x000B
+
+    CONNECTION_RESULT_NAMES = {
+        CONNECTION_SUCCESSFUL:                           'CONNECTION_SUCCESSFUL',
+        CONNECTION_PENDING:                              'CONNECTION_PENDING',
+        CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED:         'CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED',
+        CONNECTION_REFUSED_SECURITY_BLOCK:               'CONNECTION_REFUSED_SECURITY_BLOCK',
+        CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE:       'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE',
+        CONNECTION_REFUSED_INVALID_SOURCE_CID:           'CONNECTION_REFUSED_INVALID_SOURCE_CID',
+        CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED: 'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED',
+        CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS:      'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS'
+    }
+
+    @staticmethod
+    def result_name(result):
+        return name_or_number(L2CAP_Connection_Response.CONNECTION_RESULT_NAMES, result)
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('destination_cid', 2),
+    ('flags',           2),
+    ('options',         '*')
+])
+class L2CAP_Configure_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.4 CONFIGURATION REQUEST
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('source_cid', 2),
+    ('flags',      2),
+    ('result',     {'size': 2, 'mapper': lambda x: L2CAP_Configure_Response.result_name(x)}),
+    ('options',    '*')
+])
+class L2CAP_Configure_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.5 CONFIGURATION RESPONSE
+    '''
+
+    SUCCESS                         = 0x0000
+    FAILURE_UNACCEPTABLE_PARAMETERS = 0x0001
+    FAILURE_REJECTED                = 0x0002
+    FAILURE_UNKNOWN_OPTIONS         = 0x0003
+    PENDING                         = 0x0004
+    FAILURE_FLOW_SPEC_REJECTED      = 0x0005
+
+    RESULT_NAMES = {
+        SUCCESS:                         'SUCCESS',
+        FAILURE_UNACCEPTABLE_PARAMETERS: 'FAILURE_UNACCEPTABLE_PARAMETERS',
+        FAILURE_REJECTED:                'FAILURE_REJECTED',
+        FAILURE_UNKNOWN_OPTIONS:         'FAILURE_UNKNOWN_OPTIONS',
+        PENDING:                         'PENDING',
+        FAILURE_FLOW_SPEC_REJECTED:      'FAILURE_FLOW_SPEC_REJECTED'
+    }
+
+    @staticmethod
+    def result_name(result):
+        return name_or_number(L2CAP_Configure_Response.RESULT_NAMES, result)
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('destination_cid', 2),
+    ('source_cid',      2)
+])
+class L2CAP_Disconnection_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.6 DISCONNECTION REQUEST
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('destination_cid', 2),
+    ('source_cid',      2)
+])
+class L2CAP_Disconnection_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.7 DISCONNECTION RESPONSE
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('data', '*')
+])
+class L2CAP_Echo_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.8 ECHO REQUEST
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('data', '*')
+])
+class L2CAP_Echo_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.9 ECHO RESPONSE
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('info_type', {'size': 2, 'mapper': lambda x: L2CAP_Information_Request.info_type_name(x)})
+])
+class L2CAP_Information_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.10 INFORMATION REQUEST
+    '''
+
+    CONNECTIONLESS_MTU          = 0x0001
+    EXTENDED_FEATURES_SUPPORTED = 0x0002
+    FIXED_CHANNELS_SUPPORTED    = 0x0003
+
+    INFO_TYPE_NAMES = {
+        CONNECTIONLESS_MTU:          'CONNECTIONLESS_MTU',
+        EXTENDED_FEATURES_SUPPORTED: 'EXTENDED_FEATURES_SUPPORTED',
+        FIXED_CHANNELS_SUPPORTED:    'FIXED_CHANNELS_SUPPORTED'
+    }
+
+    @staticmethod
+    def info_type_name(info_type):
+        return name_or_number(L2CAP_Information_Request.INFO_TYPE_NAMES, info_type)
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('info_type', {'size': 2, 'mapper': L2CAP_Information_Request.info_type_name}),
+    ('result',    {'size': 2, 'mapper': lambda x: L2CAP_Information_Response.result_name(x)}),
+    ('data',     '*')
+])
+class L2CAP_Information_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.11 INFORMATION RESPONSE
+    '''
+    SUCCESS       = 0x00
+    NOT_SUPPORTED = 0x01
+
+    RESULT_NAMES = {
+        SUCCESS:       'SUCCESS',
+        NOT_SUPPORTED: 'NOT_SUPPORTED'
+    }
+
+    @staticmethod
+    def result_name(result):
+        return name_or_number(L2CAP_Information_Response.RESULT_NAMES, result)
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('interval_min',       2),
+    ('interval_max',       2),
+    ('slave_latency',      2),
+    ('timeout_multiplier', 2)
+])
+class L2CAP_Connection_Parameter_Update_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.20 CONNECTION PARAMETER UPDATE REQUEST
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('result', 2)
+])
+class L2CAP_Connection_Parameter_Update_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.21 CONNECTION PARAMETER UPDATE RESPONSE
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('le_psm', 2),
+    ('source_cid', 2),
+    ('mtu', 2),
+    ('mps', 2),
+    ('initial_credits', 2)
+])
+class L2CAP_LE_Credit_Based_Connection_Request(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.22 LE CREDIT BASED CONNECTION REQUEST (CODE 0x14)
+    '''
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('destination_cid', 2),
+    ('mtu',             2),
+    ('mps',             2),
+    ('initial_credits', 2),
+    ('result',          {'size': 2, 'mapper': lambda x: L2CAP_LE_Credit_Based_Connection_Response.result_name(x)})
+])
+class L2CAP_LE_Credit_Based_Connection_Response(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.23 LE CREDIT BASED CONNECTION RESPONSE (CODE 0x15)
+    '''
+
+    CONNECTION_SUCCESSFUL                               = 0x0000
+    CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED             = 0x0002
+    CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE           = 0x0004
+    CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION      = 0x0005
+    CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION       = 0x0006
+    CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0007
+    CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION          = 0x0008
+    CONNECTION_REFUSED_INVALID_SOURCE_CID               = 0x0009
+    CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED     = 0x000A
+    CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS          = 0x000B
+
+    CONNECTION_RESULT_NAMES = {
+        CONNECTION_SUCCESSFUL:                               'CONNECTION_SUCCESSFUL',
+        CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED:             'CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED',
+        CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE:           'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE',
+        CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION:      'CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION',
+        CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION:       'CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION',
+        CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE: 'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE',
+        CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION:          'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION',
+        CONNECTION_REFUSED_INVALID_SOURCE_CID:               'CONNECTION_REFUSED_INVALID_SOURCE_CID',
+        CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED:     'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED',
+        CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS:          'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS'
+    }
+
+    @staticmethod
+    def result_name(result):
+        return name_or_number(L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_RESULT_NAMES, result)
+
+
+# -----------------------------------------------------------------------------
+@L2CAP_Control_Frame.subclass([
+    ('cid', 2),
+    ('credits', 2)
+])
+class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
+    '''
+    See Bluetooth spec @ Vol 3, Part A - 4.24 LE FLOW CONTROL CREDIT (CODE 0x16)
+    '''
+
+
+# -----------------------------------------------------------------------------
+class Channel(EventEmitter):
+    # States
+    CLOSED            = 0x00
+    WAIT_CONNECT      = 0x01
+    WAIT_CONNECT_RSP  = 0x02
+    OPEN              = 0x03
+    WAIT_DISCONNECT   = 0x04
+    WAIT_CREATE       = 0x05
+    WAIT_CREATE_RSP   = 0x06
+    WAIT_MOVE         = 0x07
+    WAIT_MOVE_RSP     = 0x08
+    WAIT_MOVE_CONFIRM = 0x09
+    WAIT_CONFIRM_RSP  = 0x0A
+
+    # CONFIG substates
+    WAIT_CONFIG         = 0x10
+    WAIT_SEND_CONFIG    = 0x11
+    WAIT_CONFIG_REQ_RSP = 0x12
+    WAIT_CONFIG_RSP     = 0x13
+    WAIT_CONFIG_REQ     = 0x14
+    WAIT_IND_FINAL_RSP  = 0x15
+    WAIT_FINAL_RSP      = 0x16
+    WAIT_CONTROL_IND    = 0x17
+
+    STATE_NAMES = {
+        CLOSED:            'CLOSED',
+        WAIT_CONNECT:      'WAIT_CONNECT',
+        WAIT_CONNECT_RSP:  'WAIT_CONNECT_RSP',
+        OPEN:              'OPEN',
+        WAIT_DISCONNECT:   'WAIT_DISCONNECT',
+        WAIT_CREATE:       'WAIT_CREATE',
+        WAIT_CREATE_RSP:   'WAIT_CREATE_RSP',
+        WAIT_MOVE:         'WAIT_MOVE',
+        WAIT_MOVE_RSP:     'WAIT_MOVE_RSP',
+        WAIT_MOVE_CONFIRM: 'WAIT_MOVE_CONFIRM',
+        WAIT_CONFIRM_RSP:  'WAIT_CONFIRM_RSP',
+
+        WAIT_CONFIG:         'WAIT_CONFIG',
+        WAIT_SEND_CONFIG:    'WAIT_SEND_CONFIG',
+        WAIT_CONFIG_REQ_RSP: 'WAIT_CONFIG_REQ_RSP',
+        WAIT_CONFIG_RSP:     'WAIT_CONFIG_RSP',
+        WAIT_CONFIG_REQ:     'WAIT_CONFIG_REQ',
+        WAIT_IND_FINAL_RSP:  'WAIT_IND_FINAL_RSP',
+        WAIT_FINAL_RSP:      'WAIT_FINAL_RSP',
+        WAIT_CONTROL_IND:    'WAIT_CONTROL_IND'
+    }
+
+    def __init__(self, manager, connection, signaling_cid, psm, source_cid, mtu):
+        super().__init__()
+        self.manager           = manager
+        self.connection        = connection
+        self.signaling_cid     = signaling_cid
+        self.state             = Channel.CLOSED
+        self.mtu               = mtu
+        self.psm               = psm
+        self.source_cid        = source_cid
+        self.destination_cid   = 0
+        self.response          = None
+        self.connection_result = None
+        self.sink              = None
+
+    def change_state(self, new_state):
+        logger.debug(f'{self} state change -> {color(Channel.STATE_NAMES[new_state], "cyan")}')
+        self.state = new_state
+
+    def send_pdu(self, pdu):
+        self.manager.send_pdu(self.connection, self.destination_cid, pdu)
+
+    async def send_request(self, request):
+        # Check that there isn't already a request pending
+        if self.response:
+            raise InvalidStateError('request already pending')
+        if self.state != Channel.OPEN:
+            raise InvalidStateError('channel not open')
+
+        self.response = asyncio.get_running_loop().create_future()
+        self.send_pdu(request)
+        return await self.response
+
+    def on_pdu(self, pdu):
+        if self.response:
+            self.response.set_result(pdu)
+            self.response = None
+        elif self.sink:
+            self.sink(pdu)
+        else:
+            logger.warn(color('received pdu without a pending request or sink', 'red'))
+
+    def send_control_frame(self, frame):
+        self.manager.send_control_frame(self.connection, self.signaling_cid, frame)
+
+    async def connect(self):
+        if self.state != Channel.CLOSED:
+            raise InvalidStateError('invalid state')
+
+        self.change_state(Channel.WAIT_CONNECT_RSP)
+        self.send_control_frame(
+            L2CAP_Connection_Request(
+                identifier = self.manager.next_identifier(self.connection),
+                psm        = self.psm,
+                source_cid = self.source_cid
+            )
+        )
+
+        # Create a future to wait for the state machine to get to a success or error state
+        self.connection_result = asyncio.get_running_loop().create_future()
+        return await self.connection_result
+
+    async def disconnect(self):
+        if self.state != Channel.OPEN:
+            raise InvalidStateError('invalid state')
+
+        self.change_state(Channel.WAIT_DISCONNECT)
+        self.send_control_frame(
+            L2CAP_Disconnection_Request(
+                identifier      = self.manager.next_identifier(self.connection),
+                destination_cid = self.destination_cid,
+                source_cid      = self.source_cid
+            )
+        )
+
+        # Create a future to wait for the state machine to get to a success or error state
+        self.disconnection_result = asyncio.get_running_loop().create_future()
+        return await self.disconnection_result
+
+    def send_configure_request(self):
+        options = L2CAP_Control_Frame.encode_configuration_options([(
+            L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE,
+            struct.pack('<H', L2CAP_DEFAULT_MTU)
+        )])
+        self.send_control_frame(
+            L2CAP_Configure_Request(
+                identifier      = self.manager.next_identifier(self.connection),
+                destination_cid = self.destination_cid,
+                flags           = 0x0000,
+                options         = options
+            )
+        )
+
+    def on_connection_request(self, request):
+        self.destination_cid = request.source_cid
+        self.change_state(Channel.WAIT_CONNECT)
+        self.send_control_frame(
+            L2CAP_Connection_Response(
+                identifier      = request.identifier,
+                destination_cid = self.source_cid,
+                source_cid      = self.destination_cid,
+                result          = L2CAP_Connection_Response.CONNECTION_SUCCESSFUL,
+                status          = 0x0000
+            )
+        )
+        self.change_state(Channel.WAIT_CONFIG)
+        self.send_configure_request()
+        self.change_state(Channel.WAIT_CONFIG_REQ_RSP)
+
+    def on_connection_response(self, response):
+        if self.state != Channel.WAIT_CONNECT_RSP:
+            logger.warn(color('invalid state', 'red'))
+            return
+
+        if response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL:
+            self.destination_cid = response.destination_cid
+            self.change_state(Channel.WAIT_CONFIG)
+            self.send_configure_request()
+            self.change_state(Channel.WAIT_CONFIG_REQ_RSP)
+        elif response.result == L2CAP_Connection_Response.CONNECTION_PENDING:
+            pass
+        else:
+            self.change_state(Channel.CLOSED)
+            self.connection_result.set_exception(
+                ProtocolError(
+                    response.result,
+                    'l2cap',
+                    L2CAP_Connection_Response.result_name(response.result))
+            )
+            self.connection_result = None
+
+    def on_configure_request(self, request):
+        if (
+            self.state != Channel.WAIT_CONFIG and
+            self.state != Channel.WAIT_CONFIG_REQ and
+            self.state != Channel.WAIT_CONFIG_REQ_RSP
+        ):
+            logger.warn(color('invalid state', 'red'))
+            return
+
+        # Decode the options
+        options = L2CAP_Control_Frame.decode_configuration_options(request.options)
+        for option in options:
+            if option[0] == L2CAP_MTU_CONFIGURATION_PARAMETER_TYPE:
+                self.mtu = struct.unpack('<H', option[1])[0]
+                logger.debug(f'MTU = {self.mtu}')
+
+        self.send_control_frame(
+            L2CAP_Configure_Response(
+                identifier = request.identifier,
+                source_cid = self.destination_cid,
+                flags      = 0x0000,
+                result     = L2CAP_Configure_Response.SUCCESS,
+                options    = request.options  # TODO: don't accept everthing blindly
+            )
+        )
+        if self.state == Channel.WAIT_CONFIG:
+            self.change_state(Channel.WAIT_SEND_CONFIG)
+            self.send_configure_request()
+            self.change_state(Channel.WAIT_CONFIG_RSP)
+        elif self.state == Channel.WAIT_CONFIG_REQ:
+            self.change_state(Channel.OPEN)
+            if self.connection_result:
+                self.connection_result.set_result(None)
+                self.connection_result = None
+            self.emit('open')
+        elif self.state == Channel.WAIT_CONFIG_REQ_RSP:
+            self.change_state(Channel.WAIT_CONFIG_RSP)
+
+    def on_configure_response(self, response):
+        if response.result == L2CAP_Configure_Response.SUCCESS:
+            if self.state == Channel.WAIT_CONFIG_REQ_RSP:
+                self.change_state(Channel.WAIT_CONFIG_REQ)
+            elif self.state == Channel.WAIT_CONFIG_RSP or self.state == Channel.WAIT_CONTROL_IND:
+                self.change_state(Channel.OPEN)
+                if self.connection_result:
+                    self.connection_result.set_result(None)
+                    self.connection_result = None
+                self.emit('open')
+            else:
+                logger.warn(color('invalid state', 'red'))
+        elif response.result == L2CAP_Configure_Response.FAILURE_UNACCEPTABLE_PARAMETERS:
+            # Re-configure with what's suggested in the response
+            self.send_control_frame(
+                L2CAP_Configure_Request(
+                    identifier      = self.manager.next_identifier(self.connection),
+                    destination_cid = self.destination_cid,
+                    flags           = 0x0000,
+                    options         = response.options
+                )
+            )
+        else:
+            logger.warn(color(f'!!! configuration rejected: {L2CAP_Configure_Response.result_name(response.result)}', 'red'))
+            # TODO: decide how to fail gracefully
+
+    def on_disconnection_request(self, request):
+        if self.state == Channel.OPEN or self.state == Channel.WAIT_DISCONNECT:
+            self.send_control_frame(
+                L2CAP_Disconnection_Response(
+                    identifier      = request.identifier,
+                    destination_cid = request.destination_cid,
+                    source_cid      = request.source_cid
+                )
+            )
+            self.change_state(Channel.CLOSED)
+            self.emit('close')
+            self.manager.on_channel_closed(self)
+        else:
+            logger.warn(color('invalid state', 'red'))
+
+    def on_disconnection_response(self, response):
+        if self.state != Channel.WAIT_DISCONNECT:
+            logger.warn(color('invalid state', 'red'))
+            return
+
+        if response.destination_cid != self.destination_cid or response.source_cid != self.source_cid:
+            logger.warn('unexpected source or destination CID')
+            return
+
+        self.change_state(Channel.CLOSED)
+        if self.disconnection_result:
+            self.disconnection_result.set_result(None)
+            self.disconnection_result = None
+        self.emit('close')
+        self.manager.on_channel_closed(self)
+
+    def __str__(self):
+        return f'Channel({self.source_cid}->{self.destination_cid}, PSM={self.psm}, MTU={self.mtu}, state={Channel.STATE_NAMES[self.state]})'
+
+
+# -----------------------------------------------------------------------------
+class ChannelManager:
+    def __init__(self):
+        self.host        = None
+        self.channels    = {}  # Channels, mapped by connection and cid
+        self.identifiers = {}  # Incrementing identifier values by connection
+        self.servers     = {}  # Servers accepting connections, by PSM
+
+    def find_channel(self, connection_handle, cid):
+        if connection_channels := self.channels.get(connection_handle):
+            return connection_channels.get(cid)
+
+    @staticmethod
+    def find_free_br_edr_cid(channels):
+        # Pick the smallest valid CID that's not already in the list
+        # (not necessarily the most efficient algorithm, but the list of CID is
+        # very small in practice)
+        for cid in range(L2CAP_ACL_U_DYNAMIC_CID_RANGE_START, L2CAP_ACL_U_DYNAMIC_CID_RANGE_END + 1):
+            if cid not in channels:
+                return cid
+
+    def next_identifier(self, connection):
+        identifier = (self.identifiers.setdefault(connection.handle, 0) + 1) % 256
+        self.identifiers[connection.handle] = identifier
+        return identifier
+
+    def register_server(self, psm, server):
+        self.servers[psm] = server
+
+    def send_pdu(self, connection, cid, pdu):
+        pdu_str = pdu.hex() if type(pdu) is bytes else str(pdu)
+        logger.debug(f'{color(">>> Sending L2CAP PDU", "blue")} on connection [0x{connection.handle:04X}] (CID={cid}) {connection.peer_address}: {pdu_str}')
+        self.host.send_l2cap_pdu(connection.handle, cid, bytes(pdu))
+
+    def on_pdu(self, connection, cid, pdu):
+        if cid == L2CAP_SIGNALING_CID or cid == L2CAP_LE_SIGNALING_CID:
+            # Parse the L2CAP payload into a Control Frame object
+            control_frame = L2CAP_Control_Frame.from_bytes(pdu)
+
+            self.on_control_frame(connection, cid, control_frame)
+        else:
+            if (channel := self.find_channel(connection.handle, cid)) is None:
+                logger.warn(color(f'channel not found for 0x{connection.handle:04X}:{cid}', 'red'))
+                return
+
+            channel.on_pdu(pdu)
+
+    def send_control_frame(self, connection, cid, control_frame):
+        logger.debug(f'{color(">>> Sending L2CAP Signaling Control Frame", "blue")} on connection [0x{connection.handle:04X}] (CID={cid}) {connection.peer_address}:\n{control_frame}')
+        self.host.send_l2cap_pdu(connection.handle, cid, bytes(control_frame))
+
+    def on_control_frame(self, connection, cid, control_frame):
+        logger.debug(f'{color("<<< Received L2CAP Signaling Control Frame", "green")} on connection [0x{connection.handle:04X}] (CID={cid}) {connection.peer_address}:\n{control_frame}')
+
+        # Find the handler method
+        handler_name = f'on_{control_frame.name.lower()}'
+        handler = getattr(self, handler_name, None)
+        if handler:
+            try:
+                handler(connection, cid, control_frame)
+            except Exception as error:
+                logger.warning(f'{color("!!! Exception in handler:", "red")} {error}')
+                self.send_control_frame(
+                    connection,
+                    cid,
+                    L2CAP_Command_Reject(
+                        identifier = control_frame.identifier,
+                        reason     = L2CAP_COMMAND_NOT_UNDERSTOOD_REASON,
+                        data       = b''
+                    )
+                )
+                raise error
+        else:
+            logger.error(color('Channel Manager command not handled???', 'red'))
+            self.send_control_frame(
+                connection,
+                cid,
+                L2CAP_Command_Reject(
+                    identifier = control_frame.identifier,
+                    reason     = L2CAP_COMMAND_NOT_UNDERSTOOD_REASON,
+                    data       = b''
+                )
+            )
+
+    def on_l2cap_command_reject(self, connection, cid, packet):
+        logger.warning(f'{color("!!! Command rejected:", "red")} {packet.reason}')
+        pass
+
+    def on_l2cap_connection_request(self, connection, cid, request):
+        # Check if there's a server for this PSM
+        server = self.servers.get(request.psm)
+        if server:
+            # Find a free CID for this new channel
+            connection_channels = self.channels.setdefault(connection.handle, {})
+            source_cid = self.find_free_br_edr_cid(connection_channels)
+            if source_cid is None:  # Should never happen!
+                self.send_control_frame(
+                    connection,
+                    cid,
+                    L2CAP_Connection_Response(
+                        identifier      = request.identifier,
+                        destination_cid = request.source_cid,
+                        source_cid      = 0,
+                        result          = L2CAP_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
+                        status          = 0x0000
+                    )
+                )
+                return
+
+            # Create a new channel
+            logger.debug(f'creating server channel with cid={source_cid} for psm {request.psm}')
+            channel = Channel(self, connection, cid, request.psm, source_cid, L2CAP_MIN_BR_EDR_MTU)
+            connection_channels[source_cid] = channel
+
+            # Notify
+            server(channel)
+            channel.on_connection_request(request)
+        else:
+            logger.warn(f'No server for connection 0x{connection.handle:04X} on PSM {request.psm}')
+            self.send_control_frame(
+                connection,
+                cid,
+                L2CAP_Connection_Response(
+                    identifier      = request.identifier,
+                    destination_cid = request.source_cid,
+                    source_cid      = 0,
+                    result          = L2CAP_Connection_Response.CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED,
+                    status          = 0x0000
+                )
+            )
+
+    def on_l2cap_connection_response(self, connection, cid, response):
+        if (channel := self.find_channel(connection.handle, response.source_cid)) is None:
+            logger.warn(color(f'channel {response.source_cid} not found for 0x{connection.handle:04X}:{cid}', 'red'))
+            return
+
+        channel.on_connection_response(response)
+
+    def on_l2cap_configure_request(self, connection, cid, request):
+        if (channel := self.find_channel(connection.handle, request.destination_cid)) is None:
+            logger.warn(color(f'channel {request.destination_cid} not found for 0x{connection.handle:04X}:{cid}', 'red'))
+            return
+
+        channel.on_configure_request(request)
+
+    def on_l2cap_configure_response(self, connection, cid, response):
+        if (channel := self.find_channel(connection.handle, response.source_cid)) is None:
+            logger.warn(color(f'channel {response.source_cid} not found for 0x{connection.handle:04X}:{cid}', 'red'))
+            return
+
+        channel.on_configure_response(response)
+
+    def on_l2cap_disconnection_request(self, connection, cid, request):
+        if (channel := self.find_channel(connection.handle, request.destination_cid)) is None:
+            logger.warn(color(f'channel {request.destination_cid} not found for 0x{connection.handle:04X}:{cid}', 'red'))
+            return
+
+        channel.on_disconnection_request(request)
+
+    def on_l2cap_disconnection_response(self, connection, cid, response):
+        if (channel := self.find_channel(connection.handle, response.source_cid)) is None:
+            logger.warn(color(f'channel {response.source_cid} not found for 0x{connection.handle:04X}:{cid}', 'red'))
+            return
+
+        channel.on_disconnection_response(response)
+
+    def on_l2cap_echo_request(self, connection, cid, request):
+        logger.debug(f'<<< Echo request: data={request.data.hex()}')
+        self.send_control_frame(
+            connection,
+            cid,
+            L2CAP_Echo_Response(
+                identifier = request.identifier,
+                data       = request.data
+            )
+        )
+
+    def on_l2cap_echo_response(self, connection, cid, response):
+        logger.debug(f'<<< Echo response: data={response.data.hex()}')
+        # TODO notify listeners
+
+    def on_l2cap_information_request(self, connection, cid, request):
+        if request.info_type == L2CAP_Information_Request.CONNECTIONLESS_MTU:
+            result = L2CAP_Information_Response.SUCCESS
+            data = struct.pack('<H', 1024)  # TODO: don't use a fixed value
+        elif request.info_type == L2CAP_Information_Request.EXTENDED_FEATURES_SUPPORTED:
+            result = L2CAP_Information_Response.SUCCESS
+            data = bytes.fromhex('00000000')  # TODO: don't use a fixed value
+        elif request.info_type == L2CAP_Information_Request.FIXED_CHANNELS_SUPPORTED:
+            result = L2CAP_Information_Response.SUCCESS
+            data = bytes.fromhex('FFFFFFFFFFFFFFFF')  # TODO: don't use a fixed value
+        else:
+            result = L2CAP_Information_Request.NO_SUPPORTED
+
+        self.send_control_frame(
+            connection,
+            cid,
+            L2CAP_Information_Response(
+                identifier = request.identifier,
+                info_type  = request.info_type,
+                result     = result,
+                data       = data
+            )
+        )
+
+    def on_l2cap_connection_parameter_update_request(self, connection, cid, request):
+        if connection.role == BT_CENTRAL_ROLE:
+            self.send_control_frame(
+                connection,
+                cid,
+                L2CAP_Connection_Parameter_Update_Response(
+                    identifier = request.identifier,
+                    result     = L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT
+                )
+            )
+            self.host.send_command_sync(HCI_LE_Connection_Update_Command(
+                connection_handle   = connection.handle,
+                conn_interval_min   = request.interval_min,
+                conn_interval_max   = request.interval_max,
+                conn_latency        = request.slave_latency,
+                supervision_timeout = request.timeout_multiplier,
+                minimum_ce_length   = 0,
+                maximum_ce_length   = 0
+            ))
+        else:
+            self.send_control_frame(
+                connection,
+                cid,
+                L2CAP_Connection_Parameter_Update_Response(
+                    identifier = request.identifier,
+                    result     = L2CAP_CONNECTION_PARAMETERS_REJECTED_RESULT
+                )
+            )
+
+    def on_l2cap_connection_parameter_update_response(self, connection, cid, response):
+        pass
+
+    def on_l2cap_le_credit_based_connection_request(self, connection, cid, request):
+        # FIXME: temp fixed values
+        self.send_control_frame(
+            connection,
+            cid,
+            L2CAP_LE_Credit_Based_Connection_Response(
+                identifier      = request.identifier,
+                destination_cid = 194,  # FIXME: for testing only
+                mtu             = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU,
+                mps             = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS,
+                initial_credits = 3,  # FIXME: for testing only
+                result          = L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL
+            )
+        )
+
+    def on_l2cap_le_flow_control_credit(self, connection, cid, packet):
+        pass
+
+    def on_channel_closed(self, channel):
+        connection_channels = self.channels.get(channel.connection.handle)
+        if connection_channels:
+            if channel.source_cid in connection_channels:
+                del connection_channels[channel.source_cid]
+
+    async def connect(self, connection, psm):
+        # NOTE: this implementation hard-codes BR/EDR more
+        # TODO: LE mode (maybe?)
+
+        # Find a free CID for a new channel
+        connection_channels = self.channels.setdefault(connection.handle, {})
+        cid = self.find_free_br_edr_cid(connection_channels)
+        if cid is None:  # Should never happen!
+            raise RuntimeError('all CIDs already in use')
+
+        # Create the channel
+        logger.debug(f'creating client channel with cid={cid} for psm {psm}')
+        channel = Channel(self, connection, L2CAP_SIGNALING_CID, psm, cid, L2CAP_MIN_BR_EDR_MTU)
+        connection_channels[cid] = channel
+
+        # Connect
+        await channel.connect()
+
+        return channel
diff --git a/bumble/link.py b/bumble/link.py
new file mode 100644
index 0000000..4463e27
--- /dev/null
+++ b/bumble/link.py
@@ -0,0 +1,360 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import websockets
+from functools import partial
+from colors import color
+
+from bumble.hci import (
+    Address,
+    HCI_SUCCESS,
+    HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR,
+    HCI_CONNECTION_TIMEOUT_ERROR
+)
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+def parse_parameters(params_str):
+    result = {}
+    for param_str in params_str.split(','):
+        if '=' in param_str:
+            key, value = param_str.split('=')
+            result[key] = value
+    return result
+
+
+# -----------------------------------------------------------------------------
+# TODO: add more support for various LL exchanges (see Vol 6, Part B - 2.4 DATA CHANNEL PDU)
+# -----------------------------------------------------------------------------
+class LocalLink:
+    '''
+    Link bus for controllers to communicate with each other
+    '''
+
+    def __init__(self):
+        self.controllers        = set()
+        self.pending_connection = None
+
+    def add_controller(self, controller):
+        logger.debug(f'new controller: {controller}')
+        self.controllers.add(controller)
+
+    def remove_controller(self, controller):
+        self.controllers.remove(controller)
+
+    def find_controller(self, address):
+        for controller in self.controllers:
+            if controller.random_address == address:
+                return controller
+        return None
+
+    def on_address_changed(self, controller):
+        pass
+
+    def get_pending_connection(self):
+        return self.pending_connection
+
+    def send_advertising_data(self, sender_address, data):
+        # Send the advertising data to all controllers, except the sender
+        for controller in self.controllers:
+            if controller.random_address != sender_address:
+                controller.on_link_advertising_data(sender_address, data)
+
+    def send_acl_data(self, sender_address, destination_address, data):
+        # Send the data to the first controller with a matching address
+        if controller := self.find_controller(destination_address):
+            controller.on_link_acl_data(sender_address, data)
+
+    def on_connection_complete(self):
+        # Check that we expect this call
+        if not self.pending_connection:
+            logger.warning('on_connection_complete with no pending connection')
+            return
+
+        central_address, le_create_connection_command = self.pending_connection
+        self.pending_connection = None
+
+        # Find the controller that initiated the connection
+        if not (central_controller := self.find_controller(central_address)):
+            logger.warning('!!! Initiating controller not found')
+            return
+
+        # Connect to the first controller with a matching address
+        if peripheral_controller := self.find_controller(le_create_connection_command.peer_address):
+            central_controller.on_link_peripheral_connection_complete(le_create_connection_command, HCI_SUCCESS)
+            peripheral_controller.on_link_central_connected(central_address)
+            return
+
+        # No peripheral found
+        central_controller.on_link_peripheral_connection_complete(
+            le_create_connection_command,
+            HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR
+        )
+
+    def connect(self, central_address, le_create_connection_command):
+        logger.debug(f'$$$ CONNECTION {central_address} -> {le_create_connection_command.peer_address}')
+        self.pending_connection = (central_address, le_create_connection_command)
+        asyncio.get_running_loop().call_soon(self.on_connection_complete)
+
+    def on_disconnection_complete(self, central_address, peripheral_address, disconnect_command):
+        # Find the controller that initiated the disconnection
+        if not (central_controller := self.find_controller(central_address)):
+            logger.warning('!!! Initiating controller not found')
+            return
+
+        # Disconnect from the first controller with a matching address
+        if peripheral_controller := self.find_controller(peripheral_address):
+            peripheral_controller.on_link_central_disconnected(central_address, disconnect_command.reason)
+
+        central_controller.on_link_peripheral_disconnection_complete(disconnect_command, HCI_SUCCESS)
+
+    def disconnect(self, central_address, peripheral_address, disconnect_command):
+        logger.debug(f'$$$ DISCONNECTION {central_address} -> {peripheral_address}: reason = {disconnect_command.reason}')
+        args = [central_address, peripheral_address, disconnect_command]
+        asyncio.get_running_loop().call_soon(self.on_disconnection_complete, *args)
+
+    def on_connection_encrypted(self, central_address, peripheral_address, rand, ediv, ltk):
+        logger.debug(f'*** ENCRYPTION {central_address} -> {peripheral_address}')
+
+        if central_controller := self.find_controller(central_address):
+            central_controller.on_link_encrypted(peripheral_address, rand, ediv, ltk)
+
+        if peripheral_controller := self.find_controller(peripheral_address):
+            peripheral_controller.on_link_encrypted(central_address, rand, ediv, ltk)
+
+
+# -----------------------------------------------------------------------------
+class RemoteLink:
+    '''
+    A Link implementation that communicates with other virtual controllers via a
+    WebSocket relay
+    '''
+    def __init__(self, uri):
+        self.controller             = None
+        self.uri                    = uri
+        self.execution_queue        = asyncio.Queue()
+        self.websocket              = asyncio.get_running_loop().create_future()
+        self.rpc_result             = None
+        self.pending_connection     = None
+        self.central_connections    = set()  # List of addresses that we have connected to
+        self.peripheral_connections = set()  # List of addresses that have connected to us
+
+        # Connect and run asynchronously
+        asyncio.create_task(self.run_connection())
+        asyncio.create_task(self.run_executor_loop())
+
+    def add_controller(self, controller):
+        if self.controller:
+            raise ValueError('controller already set')
+        self.controller = controller
+
+    def remove_controller(self, controller):
+        if self.controller != controller:
+            raise ValueError('controller mismatch')
+        self.controller = None
+
+    def get_pending_connection(self):
+        return self.pending_connection
+
+    async def wait_until_connected(self):
+        await self.websocket
+
+    def execute(self, async_function):
+        self.execution_queue.put_nowait(async_function())
+
+    async def run_executor_loop(self):
+        logger.debug('executor loop starting')
+        while True:
+            item = await self.execution_queue.get()
+            try:
+                await item
+            except Exception as error:
+                logger.warning(f'{color("!!! Exception in async handler:", "red")} {error}')
+
+    async def run_connection(self):
+        # Connect to the relay
+        logger.debug(f'connecting to {self.uri}')
+        websocket = await websockets.connect(self.uri)
+        self.websocket.set_result(websocket)
+        logger.debug(f'connected to {self.uri}')
+
+        while True:
+            message = await websocket.recv()
+            logger.debug(f'received message: {message}')
+            keyword, *payload = message.split(':', 1)
+
+            handler_name = f'on_{keyword}_received'
+            handler = getattr(self, handler_name, None)
+            if handler:
+                await handler(payload[0] if payload else None)
+
+    def close(self):
+        if self.websocket.done():
+            logger.debug('closing websocket')
+            websocket = self.websocket.result()
+            asyncio.create_task(websocket.close())
+
+    async def on_result_received(self, result):
+        if self.rpc_result:
+            self.rpc_result.set_result(result)
+
+    async def on_left_received(self, address):
+        if address in self.central_connections:
+            self.controller.on_link_peripheral_disconnected(Address(address))
+            self.central_connections.remove(address)
+
+        if address in self.peripheral_connections:
+            self.controller.on_link_central_disconnected(address, HCI_CONNECTION_TIMEOUT_ERROR)
+            self.peripheral_connections.remove(address)
+
+    async def on_unreachable_received(self, target):
+        await self.on_left_received(target)
+
+    async def on_message_received(self, message):
+        sender, *payload = message.split('/', 1)
+        if payload:
+            keyword, *payload = payload[0].split(':', 1)
+            handler_name = f'on_{keyword}_message_received'
+            handler = getattr(self, handler_name, None)
+            if handler:
+                await handler(sender, payload[0] if payload else None)
+
+    async def on_advertisement_message_received(self, sender, advertisement):
+        try:
+            self.controller.on_link_advertising_data(Address(sender), bytes.fromhex(advertisement))
+        except Exception:
+            logger.exception('exception')
+
+    async def on_acl_message_received(self, sender, acl_data):
+        try:
+            self.controller.on_link_acl_data(Address(sender), bytes.fromhex(acl_data))
+        except Exception:
+            logger.exception('exception')
+
+    async def on_connect_message_received(self, sender, _):
+        # Remember the connection
+        self.peripheral_connections.add(sender)
+
+        # Notify the controller
+        logger.debug(f'connection from central {sender}')
+        self.controller.on_link_central_connected(Address(sender))
+
+        # Accept the connection by responding to it
+        await self.send_targetted_message(sender, 'connected')
+
+    async def on_connected_message_received(self, sender, _):
+        if not self.pending_connection:
+            logger.warn('received a connection ack, but no connection is pending')
+            return
+
+        # Remember the connection
+        self.central_connections.add(sender)
+
+        # Notify the controller
+        logger.debug(f'connected to peripheral {self.pending_connection.peer_address}')
+        self.controller.on_link_peripheral_connection_complete(self.pending_connection, HCI_SUCCESS)
+
+    async def on_disconnect_message_received(self, sender, message):
+        # Notify the controller
+        params = parse_parameters(message)
+        reason = int(params.get('reason', str(HCI_CONNECTION_TIMEOUT_ERROR)))
+        self.controller.on_link_central_disconnected(Address(sender), reason)
+
+        # Forget the connection
+        if sender in self.peripheral_connections:
+            self.peripheral_connections.remove(sender)
+
+    async def on_encrypted_message_received(self, sender, message):
+        # TODO parse params to get real args
+        self.controller.on_link_encrypted(Address(sender), bytes(8), 0, bytes(16))
+
+    async def send_rpc_command(self, command):
+        # Ensure we have a connection
+        websocket = await self.websocket
+
+        # Create a future value to hold the eventual result
+        assert(self.rpc_result is None)
+        self.rpc_result = asyncio.get_running_loop().create_future()
+
+        # Send the command
+        await websocket.send(command)
+
+        # Wait for the result
+        rpc_result = await self.rpc_result
+        self.rpc_result = None
+        logger.debug(f'rpc_result: {rpc_result}')
+
+        # TODO: parse the result
+
+    async def send_targetted_message(self, target, message):
+        # Ensure we have a connection
+        websocket = await self.websocket
+
+        # Send the message
+        await websocket.send(f'@{target} {message}')
+
+    async def notify_address_changed(self):
+        await self.send_rpc_command(f'/set-address {self.controller.random_address}')
+
+    def on_address_changed(self, controller):
+        logger.info(f'address changed for {controller}: {controller.random_address}')
+
+        # Notify the relay of the change
+        self.execute(self.notify_address_changed)
+
+    async def send_advertising_data_to_relay(self, data):
+        await self.send_targetted_message('*', f'advertisement:{data.hex()}')
+
+    def send_advertising_data(self, sender_address, data):
+        self.execute(partial(self.send_advertising_data_to_relay, data))
+
+    async def send_acl_data_to_relay(self, peer_address, data):
+        await self.send_targetted_message(peer_address, f'acl:{data.hex()}')
+
+    def send_acl_data(self, sender_address, peer_address, data):
+        self.execute(partial(self.send_acl_data_to_relay, peer_address, data))
+
+    async def send_connection_request_to_relay(self, peer_address):
+        await self.send_targetted_message(peer_address, 'connect')
+
+    def connect(self, central_address, le_create_connection_command):
+        if self.pending_connection:
+            logger.warn('connection already pending')
+            return
+        self.pending_connection = le_create_connection_command
+        self.execute(partial(self.send_connection_request_to_relay, str(le_create_connection_command.peer_address)))
+
+    def on_disconnection_complete(self, disconnect_command):
+        self.controller.on_link_peripheral_disconnection_complete(disconnect_command, HCI_SUCCESS)
+
+    def disconnect(self, central_address, peripheral_address, disconnect_command):
+        logger.debug(f'disconnect {central_address} -> {peripheral_address}: reason = {disconnect_command.reason}')
+        self.execute(partial(self.send_targetted_message, peripheral_address, f'disconnect:reason={disconnect_command.reason}'))
+        asyncio.get_running_loop().call_soon(self.on_disconnection_complete, disconnect_command)
+
+    def on_connection_encrypted(self, central_address, peripheral_address, rand, ediv, ltk):
+        asyncio.get_running_loop().call_soon(self.controller.on_link_encrypted, peripheral_address, rand, ediv, ltk)
+        self.execute(partial(self.send_targetted_message, peripheral_address, f'encrypted:ltk={ltk.hex()}'))
diff --git a/bumble/profiles/__init__.py b/bumble/profiles/__init__.py
new file mode 100644
index 0000000..4b7e706
--- /dev/null
+++ b/bumble/profiles/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/bumble/profiles/battery_service.py b/bumble/profiles/battery_service.py
new file mode 100644
index 0000000..a978c05
--- /dev/null
+++ b/bumble/profiles/battery_service.py
@@ -0,0 +1,61 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from ..gatt_client import ProfileServiceProxy
+from ..gatt import (
+    GATT_BATTERY_SERVICE,
+    GATT_BATTERY_LEVEL_CHARACTERISTIC,
+    TemplateService,
+    Characteristic,
+    CharacteristicValue,
+    PackedCharacteristicAdapter
+)
+
+
+# -----------------------------------------------------------------------------
+class BatteryService(TemplateService):
+    UUID = GATT_BATTERY_SERVICE
+    BATTERY_LEVEL_FORMAT = 'B'
+
+    def __init__(self, read_battery_level):
+        self.battery_level_characteristic = PackedCharacteristicAdapter(
+            Characteristic(
+                GATT_BATTERY_LEVEL_CHARACTERISTIC,
+                Characteristic.READ | Characteristic.NOTIFY,
+                Characteristic.READABLE,
+                CharacteristicValue(read=read_battery_level)
+            ),
+            format=BatteryService.BATTERY_LEVEL_FORMAT
+        )
+        super().__init__([self.battery_level_characteristic])
+
+
+# -----------------------------------------------------------------------------
+class BatteryServiceProxy(ProfileServiceProxy):
+    SERVICE_CLASS = BatteryService
+
+    def __init__(self, service_proxy):
+        self.service_proxy = service_proxy
+
+        if characteristics := service_proxy.get_characteristics_by_uuid(GATT_BATTERY_LEVEL_CHARACTERISTIC):
+            self.battery_level = PackedCharacteristicAdapter(
+                characteristics[0],
+                format=BatteryService.BATTERY_LEVEL_FORMAT
+            )
+        else:
+            self.battery_level = None
diff --git a/bumble/profiles/device_information_service.py b/bumble/profiles/device_information_service.py
new file mode 100644
index 0000000..99765b4
--- /dev/null
+++ b/bumble/profiles/device_information_service.py
@@ -0,0 +1,135 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import struct
+from typing import Tuple
+
+from ..gatt_client import ProfileServiceProxy
+from ..gatt import (
+    GATT_DEVICE_INFORMATION_SERVICE,
+    GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC,
+    GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC,
+    GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+    GATT_MODEL_NUMBER_STRING_CHARACTERISTIC,
+    GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC,
+    GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC,
+    GATT_SYSTEM_ID_CHARACTERISTIC,
+    GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
+    TemplateService,
+    Characteristic,
+    DelegatedCharacteristicAdapter,
+    UTF8CharacteristicAdapter
+)
+
+
+# -----------------------------------------------------------------------------
+class DeviceInformationService(TemplateService):
+    UUID = GATT_DEVICE_INFORMATION_SERVICE
+
+    @staticmethod
+    def pack_system_id(oui, manufacturer_id):
+        return struct.pack('<Q', oui << 40 | manufacturer_id)
+
+    @staticmethod
+    def unpack_system_id(buffer):
+        system_id = struct.unpack('<Q', buffer)[0]
+        return (system_id >> 40, system_id & 0xFFFFFFFFFF)
+
+    def __init__(
+        self,
+        manufacturer_name: str = None,
+        model_number: str = None,
+        serial_number: str = None,
+        hardware_revision: str = None,
+        firmware_revision: str = None,
+        software_revision: str = None,
+        system_id: Tuple[int, int] = None,  # (OUI, Manufacturer ID)
+        ieee_regulatory_certification_data_list: bytes = None
+        # TODO: pnp_id
+    ):
+        characteristics = [
+            Characteristic(
+                uuid,
+                Characteristic.READ,
+                Characteristic.READABLE,
+                field
+            )
+            for (field, uuid) in (
+                (manufacturer_name, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC),
+                (model_number,      GATT_MODEL_NUMBER_STRING_CHARACTERISTIC),
+                (serial_number,     GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC),
+                (hardware_revision, GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC),
+                (firmware_revision, GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC),
+                (software_revision, GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC)
+            )
+            if field is not None
+        ]
+
+        if system_id is not None:
+            characteristics.append(Characteristic(
+                GATT_SYSTEM_ID_CHARACTERISTIC,
+                Characteristic.READ,
+                Characteristic.READABLE,
+                self.pack_system_id(*system_id)
+            ))
+
+        if ieee_regulatory_certification_data_list is not None:
+            characteristics.append(Characteristic(
+                GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
+                Characteristic.READ,
+                Characteristic.READABLE,
+                ieee_regulatory_certification_data_list
+            ))
+
+        super().__init__(characteristics)
+
+
+# -----------------------------------------------------------------------------
+class DeviceInformationServiceProxy(ProfileServiceProxy):
+    SERVICE_CLASS = DeviceInformationService
+
+    def __init__(self, service_proxy):
+        self.service_proxy = service_proxy
+
+        for (field, uuid) in (
+            ('manufacturer_name', GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC),
+            ('model_number',      GATT_MODEL_NUMBER_STRING_CHARACTERISTIC),
+            ('serial_number',     GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC),
+            ('hardware_revision', GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC),
+            ('firmware_revision', GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC),
+            ('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC)
+        ):
+            if characteristics := service_proxy.get_characteristics_by_uuid(uuid):
+                characteristic = UTF8CharacteristicAdapter(characteristics[0])
+            else:
+                characteristic = None
+            self.__setattr__(field, characteristic)
+
+        if characteristics := service_proxy.get_characteristics_by_uuid(GATT_SYSTEM_ID_CHARACTERISTIC):
+            self.system_id = DelegatedCharacteristicAdapter(
+                characteristics[0],
+                encode=lambda v: DeviceInformationService.pack_system_id(*v),
+                decode=DeviceInformationService.unpack_system_id
+            )
+        else:
+            self.system_id = None
+
+        if characteristics := service_proxy.get_characteristics_by_uuid(GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC):
+            self.ieee_regulatory_certification_data_list = characteristics[0]
+        else:
+            self.ieee_regulatory_certification_data_list = None
diff --git a/bumble/rfcomm.py b/bumble/rfcomm.py
new file mode 100644
index 0000000..527eaf1
--- /dev/null
+++ b/bumble/rfcomm.py
@@ -0,0 +1,840 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+from colors import color
+
+from .utils import EventEmitter
+from .core import InvalidStateError, ProtocolError, ConnectionError
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+RFCOMM_PSM = 0x0003
+
+
+# Frame types
+RFCOMM_SABM_FRAME = 0x2F  # Control field [1,1,1,1,_,1,0,0] LSB-first
+RFCOMM_UA_FRAME   = 0x63  # Control field [0,1,1,0,_,0,1,1] LSB-first
+RFCOMM_DM_FRAME   = 0x0F  # Control field [1,1,1,1,_,0,0,0] LSB-first
+RFCOMM_DISC_FRAME = 0x43  # Control field [0,1,0,_,0,0,1,1] LSB-first
+RFCOMM_UIH_FRAME  = 0xEF  # Control field [1,1,1,_,1,1,1,1] LSB-first
+RFCOMM_UI_FRAME   = 0x03  # Control field [0,0,0,_,0,0,1,1] LSB-first
+
+RFCOMM_FRAME_TYPE_NAMES = {
+    RFCOMM_SABM_FRAME: 'SABM',
+    RFCOMM_UA_FRAME:   'UA',
+    RFCOMM_DM_FRAME:   'DM',
+    RFCOMM_DISC_FRAME: 'DISC',
+    RFCOMM_UIH_FRAME:  'UIH',
+    RFCOMM_UI_FRAME:   'UI'
+}
+
+# MCC Types
+RFCOMM_MCC_PN_TYPE  = 0x20
+RFCOMM_MCC_MSC_TYPE = 0x38
+
+# FCS CRC
+CRC_TABLE = bytes([
+    0X00, 0X91, 0XE3, 0X72, 0X07, 0X96, 0XE4, 0X75,
+    0X0E, 0X9F, 0XED, 0X7C, 0X09, 0X98, 0XEA, 0X7B,
+    0X1C, 0X8D, 0XFF, 0X6E, 0X1B, 0X8A, 0XF8, 0X69,
+    0X12, 0X83, 0XF1, 0X60, 0X15, 0X84, 0XF6, 0X67,
+    0X38, 0XA9, 0XDB, 0X4A, 0X3F, 0XAE, 0XDC, 0X4D,
+    0X36, 0XA7, 0XD5, 0X44, 0X31, 0XA0, 0XD2, 0X43,
+    0X24, 0XB5, 0XC7, 0X56, 0X23, 0XB2, 0XC0, 0X51,
+    0X2A, 0XBB, 0XC9, 0X58, 0X2D, 0XBC, 0XCE, 0X5F,
+    0X70, 0XE1, 0X93, 0X02, 0X77, 0XE6, 0X94, 0X05,
+    0X7E, 0XEF, 0X9D, 0X0C, 0X79, 0XE8, 0X9A, 0X0B,
+    0X6C, 0XFD, 0X8F, 0X1E, 0X6B, 0XFA, 0X88, 0X19,
+    0X62, 0XF3, 0X81, 0X10, 0X65, 0XF4, 0X86, 0X17,
+    0X48, 0XD9, 0XAB, 0X3A, 0X4F, 0XDE, 0XAC, 0X3D,
+    0X46, 0XD7, 0XA5, 0X34, 0X41, 0XD0, 0XA2, 0X33,
+    0X54, 0XC5, 0XB7, 0X26, 0X53, 0XC2, 0XB0, 0X21,
+    0X5A, 0XCB, 0XB9, 0X28, 0X5D, 0XCC, 0XBE, 0X2F,
+    0XE0, 0X71, 0X03, 0X92, 0XE7, 0X76, 0X04, 0X95,
+    0XEE, 0X7F, 0X0D, 0X9C, 0XE9, 0X78, 0X0A, 0X9B,
+    0XFC, 0X6D, 0X1F, 0X8E, 0XFB, 0X6A, 0X18, 0X89,
+    0XF2, 0X63, 0X11, 0X80, 0XF5, 0X64, 0X16, 0X87,
+    0XD8, 0X49, 0X3B, 0XAA, 0XDF, 0X4E, 0X3C, 0XAD,
+    0XD6, 0X47, 0X35, 0XA4, 0XD1, 0X40, 0X32, 0XA3,
+    0XC4, 0X55, 0X27, 0XB6, 0XC3, 0X52, 0X20, 0XB1,
+    0XCA, 0X5B, 0X29, 0XB8, 0XCD, 0X5C, 0X2E, 0XBF,
+    0X90, 0X01, 0X73, 0XE2, 0X97, 0X06, 0X74, 0XE5,
+    0X9E, 0X0F, 0X7D, 0XEC, 0X99, 0X08, 0X7A, 0XEB,
+    0X8C, 0X1D, 0X6F, 0XFE, 0X8B, 0X1A, 0X68, 0XF9,
+    0X82, 0X13, 0X61, 0XF0, 0X85, 0X14, 0X66, 0XF7,
+    0XA8, 0X39, 0X4B, 0XDA, 0XAF, 0X3E, 0X4C, 0XDD,
+    0XA6, 0X37, 0X45, 0XD4, 0XA1, 0X30, 0X42, 0XD3,
+    0XB4, 0X25, 0X57, 0XC6, 0XB3, 0X22, 0X50, 0XC1,
+    0XBA, 0X2B, 0X59, 0XC8, 0XBD, 0X2C, 0X5E, 0XCF
+])
+
+RFCOMM_DEFAULT_INITIAL_RX_CREDITS = 7
+RFCOMM_DEFAULT_PREFERRED_MTU      = 1280
+
+RFCOMM_DYNAMIC_CHANNEL_NUMBER_START = 1
+RFCOMM_DYNAMIC_CHANNEL_NUMBER_END   = 30
+
+
+# -----------------------------------------------------------------------------
+def fcs(buffer):
+    fcs = 0xFF
+    for byte in buffer:
+        fcs = CRC_TABLE[fcs ^ byte]
+    return 0xFF - fcs
+
+
+# -----------------------------------------------------------------------------
+class RFCOMM_Frame:
+    def __init__(self, type, c_r, dlci, p_f, information = b'', with_credits = False):
+        self.type        = type
+        self.c_r         = c_r
+        self.dlci        = dlci
+        self.p_f         = p_f
+        self.information = information
+        length = len(information)
+        if with_credits:
+            length -= 1
+        if length > 0x7F:
+            # 2-byte length indicator
+            self.length = bytes([(length & 0x7F) << 1, (length >> 7) & 0xFF])
+        else:
+            # 1-byte length indicator
+            self.length = bytes([(length << 1) | 1])
+        self.address  = (dlci << 2) | (c_r << 1) | 1
+        self.control  = type | (p_f << 4)
+        if type == RFCOMM_UIH_FRAME:
+            self.fcs = fcs(bytes([self.address, self.control]))
+        else:
+            self.fcs = fcs(bytes([self.address, self.control]) + self.length)
+
+    def type_name(self):
+        return RFCOMM_FRAME_TYPE_NAMES[self.type]
+
+    @staticmethod
+    def parse_mcc(data):
+        type = data[0] >> 2
+        c_r = (data[0] >> 1) & 1
+        length = data[1]
+        if data[1] & 1:
+            length >>= 1
+            value = data[2:]
+        else:
+            length = (data[3] << 7) & (length >> 1)
+            value = data[3:3 + length]
+
+        return (type, c_r, value)
+
+    @staticmethod
+    def make_mcc(type, c_r, data):
+        return bytes([(type << 2 | c_r << 1 | 1) & 0xFF, (len(data) & 0x7F) << 1 | 1]) + data
+
+    @staticmethod
+    def sabm(c_r, dlci):
+        return RFCOMM_Frame(RFCOMM_SABM_FRAME, c_r, dlci, 1)
+
+    @staticmethod
+    def ua(c_r, dlci):
+        return RFCOMM_Frame(RFCOMM_UA_FRAME, c_r, dlci, 1)
+
+    @staticmethod
+    def dm(c_r, dlci):
+        return RFCOMM_Frame(RFCOMM_DM_FRAME, c_r, dlci, 1)
+
+    @staticmethod
+    def disc(c_r, dlci):
+        return RFCOMM_Frame(RFCOMM_DISC_FRAME, c_r, dlci, 1)
+
+    @staticmethod
+    def uih(c_r, dlci, information, p_f = 0):
+        return RFCOMM_Frame(RFCOMM_UIH_FRAME, c_r, dlci, p_f, information, with_credits = (p_f == 1))
+
+    @staticmethod
+    def from_bytes(data):
+        # Extract fields
+        dlci = (data[0] >> 2) & 0x3F
+        c_r = (data[0] >> 1) & 0x01
+        type = data[1] & 0xEF
+        p_f = (data[1] >> 4) & 0x01
+        length = data[2]
+        if length & 0x01:
+            length >>= 1
+            information = data[3:-1]
+        else:
+            length = (data[3] << 7) & (length >> 1)
+            information = data[4:-1]
+        fcs = data[-1]
+
+        # Construct the frame and check the CRC
+        frame = RFCOMM_Frame(type, c_r, dlci, p_f, information)
+        if frame.fcs != fcs:
+            logger.warn(f'FCS mismatch: got {fcs:02X}, expected {frame.fcs:02X}')
+            raise ValueError('fcs mismatch')
+
+        return frame
+
+    def __bytes__(self):
+        return bytes([self.address, self.control]) + self.length + self.information + bytes([self.fcs])
+
+    def __str__(self):
+        return f'{color(self.type_name(), "yellow")}(c/r={self.c_r},dlci={self.dlci},p/f={self.p_f},length={len(self.information)},fcs=0x{self.fcs:02X})'
+
+
+# -----------------------------------------------------------------------------
+class RFCOMM_MCC_PN:
+    def __init__(self, dlci, cl, priority, ack_timer, max_frame_size, max_retransmissions, window_size):
+        self.dlci                = dlci
+        self.cl                  = cl
+        self.priority            = priority
+        self.ack_timer           = ack_timer
+        self.max_frame_size      = max_frame_size
+        self.max_retransmissions = max_retransmissions
+        self.window_size         = window_size
+
+    @staticmethod
+    def from_bytes(data):
+        return RFCOMM_MCC_PN(
+            dlci                = data[0],
+            cl                  = data[1],
+            priority            = data[2],
+            ack_timer           = data[3],
+            max_frame_size      = data[4] | data[5] << 8,
+            max_retransmissions = data[6],
+            window_size         = data[7]
+        )
+
+    def __bytes__(self):
+        return bytes([
+            self.dlci & 0xFF,
+            self.cl & 0xFF,
+            self.priority & 0xFF,
+            self.ack_timer & 0xFF,
+            self.max_frame_size & 0xFF,
+            (self.max_frame_size >> 8) & 0xFF,
+            self.max_retransmissions & 0xFF,
+            self.window_size & 0xFF
+        ])
+
+    def __str__(self):
+        return f'PN(dlci={self.dlci},cl={self.cl},priority={self.priority},ack_timer={self.ack_timer},max_frame_size={self.max_frame_size},max_retransmissions={self.max_retransmissions},window_size={self.window_size})'
+
+
+# -----------------------------------------------------------------------------
+class RFCOMM_MCC_MSC:
+    def __init__(self, dlci, fc, rtc, rtr, ic, dv):
+        self.dlci = dlci
+        self.fc   = fc
+        self.rtc  = rtc
+        self.rtr  = rtr
+        self.ic   = ic
+        self.dv   = dv
+
+    @staticmethod
+    def from_bytes(data):
+        return RFCOMM_MCC_MSC(
+            dlci = data[0] >> 2,
+            fc   = data[1] >> 1 & 1,
+            rtc  = data[1] >> 2 & 1,
+            rtr  = data[1] >> 3 & 1,
+            ic   = data[1] >> 6 & 1,
+            dv   = data[1] >> 7 & 1
+        )
+
+    def __bytes__(self):
+        return bytes([
+            (self.dlci << 2) | 3,
+            1 | self.fc << 1 | self.rtc << 2 | self.rtr << 3 | self.ic << 6 | self.dv << 7
+        ])
+
+    def __str__(self):
+        return f'MSC(dlci={self.dlci},fc={self.fc},rtc={self.rtc},rtr={self.rtr},ic={self.ic},dv={self.dv})'
+
+
+# -----------------------------------------------------------------------------
+class DLC(EventEmitter):
+    # States
+    INIT          = 0x00
+    CONNECTING    = 0x01
+    CONNECTED     = 0x02
+    DISCONNECTING = 0x03
+    DISCONNECTED  = 0x04
+    RESET         = 0x05
+
+    STATE_NAMES = {
+        INIT:          'INIT',
+        CONNECTING:    'CONNECTING',
+        CONNECTED:     'CONNECTED',
+        DISCONNECTING: 'DISCONNECTING',
+        DISCONNECTED:  'DISCONNECTED',
+        RESET:         'RESET'
+    }
+
+    def __init__(self, multiplexer, dlci, max_frame_size, initial_tx_credits):
+        super().__init__()
+        self.multiplexer  = multiplexer
+        self.dlci         = dlci
+        self.rx_credits   = RFCOMM_DEFAULT_INITIAL_RX_CREDITS
+        self.rx_threshold = self.rx_credits // 2
+        self.tx_credits   = initial_tx_credits
+        self.tx_buffer    = b''
+        self.state        = DLC.INIT
+        self.role         = multiplexer.role
+        self.c_r          = 1 if self.role == Multiplexer.INITIATOR else 0
+        self.sink         = None
+
+        # Compute the MTU
+        max_overhead = 4 + 1  # header with 2-byte length + fcs
+        self.mtu = min(max_frame_size, self.multiplexer.l2cap_channel.mtu - max_overhead)
+
+    @staticmethod
+    def state_name(state):
+        return DLC.STATE_NAMES[state]
+
+    def change_state(self, new_state):
+        logger.debug(f'{self} state change -> {color(self.state_name(new_state), "magenta")}')
+        self.state = new_state
+
+    def send_frame(self, frame):
+        self.multiplexer.send_frame(frame)
+
+    def on_frame(self, frame):
+        handler = getattr(self, f'on_{frame.type_name()}_frame'.lower())
+        handler(frame)
+
+    def on_sabm_frame(self, frame):
+        if self.state != DLC.CONNECTING:
+            logger.warn(color('!!! received SABM when not in CONNECTING state', 'red'))
+            return
+
+        self.send_frame(RFCOMM_Frame.ua(c_r  = 1 - self.c_r, dlci = self.dlci))
+
+        # Exchange the modem status with the peer
+        msc = RFCOMM_MCC_MSC(
+            dlci = self.dlci,
+            fc   = 0,
+            rtc  = 1,
+            rtr  = 1,
+            ic   = 0,
+            dv   = 1
+        )
+        mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_MSC_TYPE, c_r = 1, data = bytes(msc))
+        logger.debug(f'>>> MCC MSC Command: {msc}')
+        self.send_frame(
+            RFCOMM_Frame.uih(
+                c_r         = self.c_r,
+                dlci        = 0,
+                information = mcc
+            )
+        )
+
+        self.change_state(DLC.CONNECTED)
+        self.emit('open')
+
+    def on_ua_frame(self, frame):
+        if self.state != DLC.CONNECTING:
+            logger.warn(color('!!! received SABM when not in CONNECTING state', 'red'))
+            return
+
+        # Exchange the modem status with the peer
+        msc = RFCOMM_MCC_MSC(
+            dlci = self.dlci,
+            fc   = 0,
+            rtc  = 1,
+            rtr  = 1,
+            ic   = 0,
+            dv   = 1
+        )
+        mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_MSC_TYPE, c_r = 1, data = bytes(msc))
+        logger.debug(f'>>> MCC MSC Command: {msc}')
+        self.send_frame(
+            RFCOMM_Frame.uih(
+                c_r         = self.c_r,
+                dlci        = 0,
+                information = mcc
+            )
+        )
+
+        self.change_state(DLC.CONNECTED)
+        self.multiplexer.on_dlc_open_complete(self)
+
+    def on_dm_frame(self, frame):
+        # TODO: handle all states
+        pass
+
+    def on_disc_frame(self, frame):
+        # TODO: handle all states
+        self.send_frame(RFCOMM_Frame.ua(c_r = 1 - self.c_r, dlci = self.dlci))
+
+    def on_uih_frame(self, frame):
+        data = frame.information
+        if frame.p_f == 1:
+            # With credits
+            credits = frame.information[0]
+            self.tx_credits += credits
+
+            logger.debug(f'<<< Credits [{self.dlci}]: received {credits}, total={self.tx_credits}')
+            data = data[1:]
+
+        logger.debug(f'{color("<<< Data", "yellow")} [{self.dlci}] {len(data)} bytes, rx_credits={self.rx_credits}: {data.hex()}')
+        if len(data) and self.sink:
+            self.sink(data)
+
+        # Update the credits
+        if self.rx_credits > 0:
+            self.rx_credits -= 1
+        else:
+            logger.warn(color('!!! received frame with no rx credits', 'red'))
+
+        # Check if there's anything to send (including credits)
+        self.process_tx()
+
+    def on_ui_frame(self, frame):
+        pass
+
+    def on_mcc_msc(self, c_r, msc):
+        if c_r:
+            # Command
+            logger.debug(f'<<< MCC MSC Command: {msc}')
+            msc = RFCOMM_MCC_MSC(
+                dlci = self.dlci,
+                fc   = 0,
+                rtc  = 1,
+                rtr  = 1,
+                ic   = 0,
+                dv   = 1
+            )
+            mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_MSC_TYPE, c_r = 0, data = bytes(msc))
+            logger.debug(f'>>> MCC MSC Response: {msc}')
+            self.send_frame(
+                RFCOMM_Frame.uih(
+                    c_r         = self.c_r,
+                    dlci        = 0,
+                    information = mcc
+                )
+            )
+        else:
+            # Response
+            logger.debug(f'<<< MCC MSC Response: {msc}')
+
+    def connect(self):
+        if not self.state == DLC.INIT:
+            raise InvalidStateError('invalid state')
+
+        self.change_state(DLC.CONNECTING)
+        self.connection_result = asyncio.get_running_loop().create_future()
+        self.send_frame(
+            RFCOMM_Frame.sabm(
+                c_r  = self.c_r,
+                dlci = self.dlci
+            )
+        )
+
+    def accept(self):
+        if not self.state == DLC.INIT:
+            raise InvalidStateError('invalid state')
+
+        pn = RFCOMM_MCC_PN(
+            dlci                = self.dlci,
+            cl                  = 0xE0,
+            priority            = 7,
+            ack_timer           = 0,
+            max_frame_size      = RFCOMM_DEFAULT_PREFERRED_MTU,
+            max_retransmissions = 0,
+            window_size         = RFCOMM_DEFAULT_INITIAL_RX_CREDITS
+        )
+        mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_PN_TYPE, c_r = 0, data = bytes(pn))
+        logger.debug(f'>>> PN Response: {pn}')
+        self.send_frame(
+            RFCOMM_Frame.uih(
+                c_r         = self.c_r,
+                dlci        = 0,
+                information = mcc
+            )
+        )
+        self.change_state(DLC.CONNECTING)
+
+    def rx_credits_needed(self):
+        if self.rx_credits <= self.rx_threshold:
+            return RFCOMM_DEFAULT_INITIAL_RX_CREDITS - self.rx_credits
+        else:
+            return 0
+
+    def process_tx(self):
+        # Send anything we can (or an empty frame if we need to send rx credits)
+        rx_credits_needed = self.rx_credits_needed()
+        while (self.tx_buffer and self.tx_credits > 0) or rx_credits_needed > 0:
+            # Get the next chunk, up to MTU size
+            if rx_credits_needed > 0:
+                chunk = bytes([rx_credits_needed]) + self.tx_buffer[:self.mtu - 1]
+                self.tx_buffer = self.tx_buffer[len(chunk) - 1:]
+                self.rx_credits += rx_credits_needed
+                tx_credit_spent = (len(chunk) > 1)
+            else:
+                chunk = self.tx_buffer[:self.mtu]
+                self.tx_buffer = self.tx_buffer[len(chunk):]
+                tx_credit_spent = True
+
+            # Update the tx credits
+            # (no tx credit spent for empty frames that only contain rx credits)
+            if tx_credit_spent:
+                self.tx_credits -= 1
+
+            # Send the frame
+            logger.debug(f'>>> sending {len(chunk)} bytes with {rx_credits_needed} credits, rx_credits={self.rx_credits}, tx_credits={self.tx_credits}')
+            self.send_frame(
+                RFCOMM_Frame.uih(
+                    c_r          = self.c_r,
+                    dlci         = self.dlci,
+                    information  = chunk,
+                    p_f          = 1 if rx_credits_needed > 0 else 0
+                )
+            )
+
+            rx_credits_needed = 0
+
+    # Stream protocol
+    def write(self, data):
+        # We can only send bytes
+        if type(data) != bytes:
+            if type(data) == str:
+                # Automatically convert strings to bytes using UTF-8
+                data = data.encode('utf-8')
+            else:
+                raise ValueError('write only accept bytes or strings')
+
+        self.tx_buffer += data
+        self.process_tx()
+
+    def drain(self):
+        # TODO
+        pass
+
+    def __str__(self):
+        return f'DLC(dlci={self.dlci},state={self.state_name(self.state)})'
+
+
+# -----------------------------------------------------------------------------
+class Multiplexer(EventEmitter):
+    # Roles
+    INITIATOR = 0x00
+    RESPONDER = 0x01
+
+    # States
+    INIT          = 0x00
+    CONNECTING    = 0x01
+    CONNECTED     = 0x02
+    OPENING       = 0x03
+    DISCONNECTING = 0x04
+    DISCONNECTED  = 0x05
+    RESET         = 0x06
+
+    STATE_NAMES = {
+        INIT:          'INIT',
+        CONNECTING:    'CONNECTING',
+        CONNECTED:     'CONNECTED',
+        OPENING:       'OPENING',
+        DISCONNECTING: 'DISCONNECTING',
+        DISCONNECTED:  'DISCONNECTED',
+        RESET:         'RESET'
+    }
+
+    def __init__(self, l2cap_channel, role):
+        super().__init__()
+        self.role                 = role
+        self.l2cap_channel        = l2cap_channel
+        self.state                = Multiplexer.INIT
+        self.dlcs                 = {}  # DLCs, by DLCI
+        self.connection_result    = None
+        self.disconnection_result = None
+        self.open_result          = None
+        self.acceptor             = None
+
+        # Become a sink for the L2CAP channel
+        l2cap_channel.sink = self.on_pdu
+
+    @staticmethod
+    def state_name(state):
+        return Multiplexer.STATE_NAMES[state]
+
+    def change_state(self, new_state):
+        logger.debug(f'{self} state change -> {color(self.state_name(new_state), "cyan")}')
+        self.state = new_state
+
+    def send_frame(self, frame):
+        logger.debug(f'>>> Multiplexer sending {frame}')
+        self.l2cap_channel.send_pdu(frame)
+
+    def on_pdu(self, pdu):
+        frame = RFCOMM_Frame.from_bytes(pdu)
+        logger.debug(f'<<< Multiplexer received {frame}')
+
+        # Dispatch to this multiplexer or to a dlc, depending on the address
+        if frame.dlci == 0:
+            self.on_frame(frame)
+        else:
+            if frame.type == RFCOMM_DM_FRAME:
+                # DM responses are for a DLCI, but since we only create the dlc when we receive
+                # a PN response (because we need the parameters), we handle DM frames at the Multiplexer
+                # level
+                self.on_dm_frame(frame)
+            else:
+                dlc = self.dlcs.get(frame.dlci)
+                if dlc is None:
+                    logger.warn(f'no dlc for DLCI {frame.dlci}')
+                    return
+                dlc.on_frame(frame)
+
+    def on_frame(self, frame):
+        handler = getattr(self, f'on_{frame.type_name()}_frame'.lower())
+        handler(frame)
+
+    def on_sabm_frame(self, frame):
+        if self.state != Multiplexer.INIT:
+            logger.debug('not in INIT state, ignoring SABM')
+            return
+        self.change_state(Multiplexer.CONNECTED)
+        self.send_frame(RFCOMM_Frame.ua(c_r = 1, dlci = 0))
+
+    def on_ua_frame(self, frame):
+        if self.state == Multiplexer.CONNECTING:
+            self.change_state(Multiplexer.CONNECTED)
+            if self.connection_result:
+                self.connection_result.set_result(0)
+                self.connection_result = None
+        elif self.state == Multiplexer.DISCONNECTING:
+            self.change_state(Multiplexer.DISCONNECTED)
+            if self.disconnection_result:
+                self.disconnection_result.set_result(None)
+                self.disconnection_result = None
+
+    def on_dm_frame(self, frame):
+        if self.state == Multiplexer.OPENING:
+            self.change_state(Multiplexer.CONNECTED)
+            if self.open_result:
+                self.open_result.set_exception(ConnectionError(ConnectionError.CONNECTION_REFUSED))
+        else:
+            logger.warn(f'unexpected state for DM: {self}')
+
+    def on_disc_frame(self, frame):
+        self.change_state(Multiplexer.DISCONNECTED)
+        self.send_frame(RFCOMM_Frame.ua(c_r = 0 if self.role == Multiplexer.INITIATOR else 1, dlci = 0))
+
+    def on_uih_frame(self, frame):
+        (type, c_r, value) = RFCOMM_Frame.parse_mcc(frame.information)
+
+        if type == RFCOMM_MCC_PN_TYPE:
+            pn = RFCOMM_MCC_PN.from_bytes(value)
+            self.on_mcc_pn(c_r, pn)
+        elif type == RFCOMM_MCC_MSC_TYPE:
+            mcs = RFCOMM_MCC_MSC.from_bytes(value)
+            self.on_mcc_msc(c_r, mcs)
+
+    def on_ui_frame(self, frame):
+        pass
+
+    def on_mcc_pn(self, c_r, pn):
+        if c_r == 1:
+            # Command
+            logger.debug(f'<<< PN Command: {pn}')
+
+            # Check with the multiplexer if there's an acceptor for this channel
+            if pn.dlci & 1:
+                # Not expected, this is an initiator-side number
+                # TODO: error out
+                logger.warn(f'invalid DLCI: {pn.dlci}')
+            else:
+                if self.acceptor:
+                    channel_number = pn.dlci >> 1
+                    if self.acceptor(channel_number):
+                        # Create a new DLC
+                        dlc = DLC(self, pn.dlci, pn.max_frame_size, pn.window_size)
+                        self.dlcs[pn.dlci] = dlc
+
+                        # Re-emit the handshake completion event
+                        dlc.on('open', lambda: self.emit('dlc', dlc))
+
+                        # Respond to complete the handshake
+                        dlc.accept()
+                    else:
+                        # No acceptor, we're in Disconnected Mode
+                        self.send_frame(RFCOMM_Frame.dm(c_r = 1, dlci = pn.dlci))
+                else:
+                    # No acceptor?? shouldn't happen
+                    logger.warn(color('!!! no acceptor registered', 'red'))
+        else:
+            # Response
+            logger.debug(f'>>> PN Response: {pn}')
+            if self.state == Multiplexer.OPENING:
+                dlc = DLC(self, pn.dlci, pn.max_frame_size, pn.window_size)
+                self.dlcs[pn.dlci] = dlc
+                dlc.connect()
+            else:
+                logger.warn('ignoring PN response')
+
+    def on_mcc_msc(self, c_r, msc):
+        dlc = self.dlcs.get(msc.dlci)
+        if dlc is None:
+            logger.warn(f'no dlc for DLCI {msc.dlci}')
+            return
+        dlc.on_mcc_msc(c_r, msc)
+
+    async def connect(self):
+        if self.state != Multiplexer.INIT:
+            raise InvalidStateError('invalid state')
+
+        self.change_state(Multiplexer.CONNECTING)
+        self.connection_result = asyncio.get_running_loop().create_future()
+        self.send_frame(RFCOMM_Frame.sabm(c_r = 1, dlci = 0))
+        return await self.connection_result
+
+    async def disconnect(self):
+        if self.state != Multiplexer.CONNECTED:
+            return
+
+        self.disconnection_result = asyncio.get_running_loop().create_future()
+        self.change_state(Multiplexer.DISCONNECTING)
+        self.send_frame(RFCOMM_Frame.disc(c_r = 1 if self.role == Multiplexer.INITIATOR else 0, dlci = 0))
+        await self.disconnection_result
+
+    async def open_dlc(self, channel):
+        if self.state != Multiplexer.CONNECTED:
+            if self.state == Multiplexer.OPENING:
+                raise InvalidStateError('open already in progress')
+            else:
+                raise InvalidStateError('not connected')
+
+        pn = RFCOMM_MCC_PN(
+            dlci                = channel << 1,
+            cl                  = 0xF0,
+            priority            = 7,
+            ack_timer           = 0,
+            max_frame_size      = RFCOMM_DEFAULT_PREFERRED_MTU,
+            max_retransmissions = 0,
+            window_size         = RFCOMM_DEFAULT_INITIAL_RX_CREDITS
+        )
+        mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_PN_TYPE, c_r = 1, data = bytes(pn))
+        logger.debug(f'>>> Sending MCC: {pn}')
+        self.open_result = asyncio.get_running_loop().create_future()
+        self.change_state(Multiplexer.OPENING)
+        self.send_frame(
+            RFCOMM_Frame.uih(
+                c_r         = 1 if self.role == Multiplexer.INITIATOR else 0,
+                dlci        = 0,
+                information = mcc
+            )
+        )
+        result = await self.open_result
+        self.open_result = None
+        return result
+
+    def on_dlc_open_complete(self, dlc):
+        logger.debug(f'DLC [{dlc.dlci}] open complete')
+        self.change_state(Multiplexer.CONNECTED)
+        if self.open_result:
+            self.open_result.set_result(dlc)
+
+    def __str__(self):
+        return f'Multiplexer(state={self.state_name(self.state)})'
+
+
+# -----------------------------------------------------------------------------
+class Client:
+    def __init__(self, device, connection):
+        self.device        = device
+        self.connection    = connection
+        self.l2cap_channel = None
+        self.multiplexer   = None
+
+    async def start(self):
+        # Create a new L2CAP connection
+        try:
+            self.l2cap_channel = await self.device.l2cap_channel_manager.connect(self.connection, RFCOMM_PSM)
+        except ProtocolError as error:
+            logger.warn(f'L2CAP connection failed: {error}')
+            raise
+
+        # Create a mutliplexer to manage DLCs with the server
+        self.multiplexer = Multiplexer(self.l2cap_channel, Multiplexer.INITIATOR)
+
+        # Connect the multiplexer
+        await self.multiplexer.connect()
+
+        return self.multiplexer
+
+    async def shutdown(self):
+        # Disconnect the multiplexer
+        await self.multiplexer.disconnect()
+        self.multiplexer = None
+
+        # Close the L2CAP channel
+        # TODO
+
+
+# -----------------------------------------------------------------------------
+class Server(EventEmitter):
+    def __init__(self, device):
+        super().__init__()
+        self.device      = device
+        self.multiplexer = None
+        self.acceptors   = {}
+
+        # Register ourselves with the L2CAP channel manager
+        device.register_l2cap_server(RFCOMM_PSM, self.on_connection)
+
+    def listen(self, acceptor):
+        # Find a free channel number
+        for channel in range(RFCOMM_DYNAMIC_CHANNEL_NUMBER_START, RFCOMM_DYNAMIC_CHANNEL_NUMBER_END + 1):
+            if channel not in self.acceptors:
+                self.acceptors[channel] = acceptor
+                return channel
+
+        # All channels used...
+        return 0
+
+    def on_connection(self, l2cap_channel):
+        logger.debug(f'+++ new L2CAP connection: {l2cap_channel}')
+        l2cap_channel.on('open', lambda: self.on_l2cap_channel_open(l2cap_channel))
+
+    def on_l2cap_channel_open(self, l2cap_channel):
+        logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}')
+
+        # Create a new multiplexer for the channel
+        multiplexer = Multiplexer(l2cap_channel, Multiplexer.RESPONDER)
+        multiplexer.acceptor = self.accept_dlc
+        multiplexer.on('dlc', self.on_dlc)
+
+        # Notify
+        self.emit('start', multiplexer)
+
+    def accept_dlc(self, channel_number):
+        return channel_number in self.acceptors
+
+    def on_dlc(self, dlc):
+        logger.debug(f'@@@ new DLC connected: {dlc}')
+
+        # Let the acceptor know
+        acceptor = self.acceptors.get(dlc.dlci >> 1)
+        if acceptor:
+            acceptor(dlc)
diff --git a/bumble/sdp.py b/bumble/sdp.py
new file mode 100644
index 0000000..935561e
--- /dev/null
+++ b/bumble/sdp.py
@@ -0,0 +1,1021 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import struct
+from colors import color
+import colors
+
+from . import core
+from .core import InvalidStateError
+from .hci import HCI_Object, name_or_number, key_with_value
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+SDP_CONTINUATION_WATCHDOG = 64  # Maximum number of continuations we're willing to do
+
+SDP_PSM = 0x0001
+
+SDP_ERROR_RESPONSE                    = 0x01
+SDP_SERVICE_SEARCH_REQUEST            = 0x02
+SDP_SERVICE_SEARCH_RESPONSE           = 0x03
+SDP_SERVICE_ATTRIBUTE_REQUEST         = 0x04
+SDP_SERVICE_ATTRIBUTE_RESPONSE        = 0x05
+SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST  = 0x06
+SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE = 0x07
+
+SDP_PDU_NAMES = {
+    SDP_ERROR_RESPONSE:                    'SDP_ERROR_RESPONSE',
+    SDP_SERVICE_SEARCH_REQUEST:            'SDP_SERVICE_SEARCH_REQUEST',
+    SDP_SERVICE_SEARCH_RESPONSE:           'SDP_SERVICE_SEARCH_RESPONSE',
+    SDP_SERVICE_ATTRIBUTE_REQUEST:         'SDP_SERVICE_ATTRIBUTE_REQUEST',
+    SDP_SERVICE_ATTRIBUTE_RESPONSE:        'SDP_SERVICE_ATTRIBUTE_RESPONSE',
+    SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST:  'SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST',
+    SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE: 'SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE'
+}
+
+SDP_INVALID_SDP_VERSION_ERROR                       = 0x0001
+SDP_INVALID_SERVICE_RECORD_HANDLE_ERROR             = 0x0002
+SDP_INVALID_REQUEST_SYNTAX_ERROR                    = 0x0003
+SDP_INVALID_PDU_SIZE_ERROR                          = 0x0004
+SDP_INVALID_CONTINUATION_STATE_ERROR                = 0x0005
+SDP_INSUFFICIENT_RESOURCES_TO_SATISFY_REQUEST_ERROR = 0x0006
+
+SDP_ERROR_NAMES = {
+    SDP_INVALID_SDP_VERSION_ERROR:                       'SDP_INVALID_SDP_VERSION_ERROR',
+    SDP_INVALID_SERVICE_RECORD_HANDLE_ERROR:             'SDP_INVALID_SERVICE_RECORD_HANDLE_ERROR',
+    SDP_INVALID_REQUEST_SYNTAX_ERROR:                    'SDP_INVALID_REQUEST_SYNTAX_ERROR',
+    SDP_INVALID_PDU_SIZE_ERROR:                          'SDP_INVALID_PDU_SIZE_ERROR',
+    SDP_INVALID_CONTINUATION_STATE_ERROR:                'SDP_INVALID_CONTINUATION_STATE_ERROR',
+    SDP_INSUFFICIENT_RESOURCES_TO_SATISFY_REQUEST_ERROR: 'SDP_INSUFFICIENT_RESOURCES_TO_SATISFY_REQUEST_ERROR'
+}
+
+SDP_SERVICE_NAME_ATTRIBUTE_ID_OFFSET        = 0x0000
+SDP_SERVICE_DESCRIPTION_ATTRIBUTE_ID_OFFSET = 0x0001
+SDP_PROVIDER_NAME_ATTRIBUTE_ID_OFFSET       = 0x0002
+
+SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID               = 0X0000
+SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID               = 0X0001
+SDP_SERVICE_RECORD_STATE_ATTRIBUTE_ID                = 0X0002
+SDP_SERVICE_ID_ATTRIBUTE_ID                          = 0X0003
+SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID            = 0X0004
+SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID                   = 0X0005
+SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID     = 0X0006
+SDP_SERVICE_INFO_TIME_TO_LIVE_ATTRIBUTE_ID           = 0X0007
+SDP_SERVICE_AVAILABILITY_ATTRIBUTE_ID                = 0X0008
+SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID   = 0X0009
+SDP_DOCUMENTATION_URL_ATTRIBUTE_ID                   = 0X000A
+SDP_CLIENT_EXECUTABLE_URL_ATTRIBUTE_ID               = 0X000B
+SDP_ICON_URL_ATTRIBUTE_ID                            = 0X000C
+SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID = 0X000D
+
+SDP_ATTRIBUTE_ID_NAMES = {
+    SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID:               'SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID',
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID:               'SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID',
+    SDP_SERVICE_RECORD_STATE_ATTRIBUTE_ID:                'SDP_SERVICE_RECORD_STATE_ATTRIBUTE_ID',
+    SDP_SERVICE_ID_ATTRIBUTE_ID:                          'SDP_SERVICE_ID_ATTRIBUTE_ID',
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:            'SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID',
+    SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID:                   'SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID',
+    SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID:     'SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID',
+    SDP_SERVICE_INFO_TIME_TO_LIVE_ATTRIBUTE_ID:           'SDP_SERVICE_INFO_TIME_TO_LIVE_ATTRIBUTE_ID',
+    SDP_SERVICE_AVAILABILITY_ATTRIBUTE_ID:                'SDP_SERVICE_AVAILABILITY_ATTRIBUTE_ID',
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID:   'SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID',
+    SDP_DOCUMENTATION_URL_ATTRIBUTE_ID:                   'SDP_DOCUMENTATION_URL_ATTRIBUTE_ID',
+    SDP_CLIENT_EXECUTABLE_URL_ATTRIBUTE_ID:               'SDP_CLIENT_EXECUTABLE_URL_ATTRIBUTE_ID',
+    SDP_ICON_URL_ATTRIBUTE_ID:                            'SDP_ICON_URL_ATTRIBUTE_ID',
+    SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID: 'SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID'
+}
+
+SDP_PUBLIC_BROWSE_ROOT = core.UUID.from_16_bits(0x1002, 'PublicBrowseRoot')
+
+# To be used in searches where an attribute ID list allows a range to be specified
+SDP_ALL_ATTRIBUTES_RANGE = (0x0000FFFF, 4)  # Express this as tuple so we can convey the desired encoding size
+
+
+# -----------------------------------------------------------------------------
+class DataElement:
+    NIL              = 0
+    UNSIGNED_INTEGER = 1
+    SIGNED_INTEGER   = 2
+    UUID             = 3
+    TEXT_STRING      = 4
+    BOOLEAN          = 5
+    SEQUENCE         = 6
+    ALTERNATIVE      = 7
+    URL              = 8
+
+    TYPE_NAMES = {
+        NIL:              'NIL',
+        UNSIGNED_INTEGER: 'UNSIGNED_INTEGER',
+        SIGNED_INTEGER:   'SIGNED_INTEGER',
+        UUID:             'UUID',
+        TEXT_STRING:      'TEXT_STRING',
+        BOOLEAN:          'BOOLEAN',
+        SEQUENCE:         'SEQUENCE',
+        ALTERNATIVE:      'ALTERNATIVE',
+        URL:              'URL'
+    }
+
+    type_constructors = {
+        NIL:              lambda x: DataElement(DataElement.NIL, None),
+        UNSIGNED_INTEGER: lambda x, y: DataElement(DataElement.UNSIGNED_INTEGER, DataElement.unsigned_integer_from_bytes(x), value_size=y),
+        SIGNED_INTEGER:   lambda x, y: DataElement(DataElement.SIGNED_INTEGER, DataElement.signed_integer_from_bytes(x), value_size=y),
+        UUID:             lambda x: DataElement(DataElement.UUID, core.UUID.from_bytes(bytes(reversed(x)))),
+        TEXT_STRING:      lambda x: DataElement(DataElement.TEXT_STRING, x.decode('utf8')),
+        BOOLEAN:          lambda x: DataElement(DataElement.BOOLEAN, x[0] == 1),
+        SEQUENCE:         lambda x: DataElement(DataElement.SEQUENCE, DataElement.list_from_bytes(x)),
+        ALTERNATIVE:      lambda x: DataElement(DataElement.ALTERNATIVE, DataElement.list_from_bytes(x)),
+        URL:              lambda x: DataElement(DataElement.URL, x.decode('utf8'))
+    }
+
+    def __init__(self, type, value, value_size=None):
+        self.type       = type
+        self.value      = value
+        self.value_size = value_size
+        self.bytes      = None  # Used a cache when parsing from bytes so we can emit a byte-for-byte replica
+        if type == DataElement.UNSIGNED_INTEGER or type == DataElement.SIGNED_INTEGER:
+            if value_size is None:
+                raise ValueError('integer types must have a value size specified')
+
+    @staticmethod
+    def nil():
+        return DataElement(DataElement.NIL, None)
+
+    @staticmethod
+    def unsigned_integer(value, value_size):
+        return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size)
+
+    @staticmethod
+    def unsigned_integer_8(value):
+        return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size=1)
+
+    @staticmethod
+    def unsigned_integer_16(value):
+        return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size=2)
+
+    @staticmethod
+    def unsigned_integer_32(value):
+        return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size=4)
+
+    @staticmethod
+    def signed_integer(value, value_size):
+        return DataElement(DataElement.SIGNED_INTEGER, value, value_size)
+
+    @staticmethod
+    def signed_integer_8(value):
+        return DataElement(DataElement.SIGNED_INTEGER, value, value_size=1)
+
+    @staticmethod
+    def signed_integer_16(value):
+        return DataElement(DataElement.SIGNED_INTEGER, value, value_size=2)
+
+    @staticmethod
+    def signed_integer_32(value):
+        return DataElement(DataElement.SIGNED_INTEGER, value, value_size=4)
+
+    @staticmethod
+    def uuid(value):
+        return DataElement(DataElement.UUID, value)
+
+    @staticmethod
+    def text_string(value):
+        return DataElement(DataElement.TEXT_STRING, value)
+
+    @staticmethod
+    def boolean(value):
+        return DataElement(DataElement.BOOLEAN, value)
+
+    @staticmethod
+    def sequence(value):
+        return DataElement(DataElement.SEQUENCE, value)
+
+    @staticmethod
+    def alternative(value):
+        return DataElement(DataElement.ALTERNATIVE, value)
+
+    @staticmethod
+    def url(value):
+        return DataElement(DataElement.URL, value)
+
+    @staticmethod
+    def unsigned_integer_from_bytes(data):
+        if len(data) == 1:
+            return data[0]
+        elif len(data) == 2:
+            return struct.unpack('>H', data)[0]
+        elif len(data) == 4:
+            return struct.unpack('>I', data)[0]
+        elif len(data) == 8:
+            return struct.unpack('>Q', data)[0]
+        else:
+            raise ValueError(f'invalid integer length {len(data)}')
+
+    @staticmethod
+    def signed_integer_from_bytes(data):
+        if len(data) == 1:
+            return struct.unpack('b', data)[0]
+        elif len(data) == 2:
+            return struct.unpack('>h', data)[0]
+        elif len(data) == 4:
+            return struct.unpack('>i', data)[0]
+        elif len(data) == 8:
+            return struct.unpack('>q', data)[0]
+        else:
+            raise ValueError(f'invalid integer length {len(data)}')
+
+    @staticmethod
+    def list_from_bytes(data):
+        elements = []
+        while data:
+            element = DataElement.from_bytes(data)
+            elements.append(element)
+            data = data[len(bytes(element)):]
+        return elements
+
+    @staticmethod
+    def parse_from_bytes(data, offset):
+        element = DataElement.from_bytes(data[offset:])
+        return offset + len(bytes(element)), element
+
+    @staticmethod
+    def from_bytes(data):
+        type = data[0] >> 3
+        size_index  = data[0] & 7
+        value_offset = 0
+        if size_index == 0:
+            if type == DataElement.NIL:
+                value_size = 0
+            else:
+                value_size = 1
+        elif size_index == 1:
+            value_size = 2
+        elif size_index == 2:
+            value_size = 4
+        elif size_index == 3:
+            value_size = 8
+        elif size_index == 4:
+            value_size = 16
+        elif size_index == 5:
+            value_size = data[1]
+            value_offset = 1
+        elif size_index == 6:
+            value_size = struct.unpack('>H', data[1:3])[0]
+            value_offset = 2
+        else:  # size_index == 7
+            value_size = struct.unpack('>I', data[1:5])[0]
+            value_offset = 4
+
+        value_data = data[1 + value_offset:1 + value_offset + value_size]
+        constructor = DataElement.type_constructors.get(type)
+        if constructor:
+            if type == DataElement.UNSIGNED_INTEGER or type == DataElement.SIGNED_INTEGER:
+                result = constructor(value_data, value_size)
+            else:
+                result = constructor(value_data)
+        else:
+            result = DataElement(type, value_data)
+        result.bytes = data[:1 + value_offset + value_size]  # Keep a copy so we can re-serialize to an exact replica
+        return result
+
+    def to_bytes(self):
+        return bytes(self)
+
+    def __bytes__(self):
+        # Return early if we have a cache
+        if self.bytes:
+            return self.bytes
+
+        if self.type == DataElement.NIL:
+            data = b''
+        elif self.type == DataElement.UNSIGNED_INTEGER:
+            if self.value < 0:
+                raise ValueError('UNSIGNED_INTEGER cannot be negative')
+            elif self.value_size == 1:
+                data = struct.pack('B', self.value)
+            elif self.value_size == 2:
+                data = struct.pack('>H', self.value)
+            elif self.value_size == 4:
+                data = struct.pack('>I', self.value)
+            elif self.value_size == 8:
+                data = struct.pack('>Q', self.value)
+            else:
+                raise ValueError('invalid value_size')
+        elif self.type == DataElement.SIGNED_INTEGER:
+            if self.value_size == 1:
+                data = struct.pack('b', self.value)
+            elif self.value_size == 2:
+                data = struct.pack('>h', self.value)
+            elif self.value_size == 4:
+                data = struct.pack('>i', self.value)
+            elif self.value_size == 8:
+                data = struct.pack('>q', self.value)
+            else:
+                raise ValueError('invalid value_size')
+        elif self.type == DataElement.UUID:
+            data = bytes(reversed(bytes(self.value)))
+        elif self.type == DataElement.TEXT_STRING or self.type == DataElement.URL:
+            data = self.value.encode('utf8')
+        elif self.type == DataElement.BOOLEAN:
+            data = bytes([1 if self.value else 0])
+        elif self.type == DataElement.SEQUENCE or self.type == DataElement.ALTERNATIVE:
+            data = b''.join([bytes(element) for element in self.value])
+        else:
+            data = self.value
+
+        size = len(data)
+        size_bytes = b''
+        if self.type == DataElement.NIL:
+            if size != 0:
+                raise ValueError('NIL must be empty')
+            size_index = 0
+        elif (self.type == DataElement.UNSIGNED_INTEGER or
+              self.type == DataElement.SIGNED_INTEGER or
+              self.type == DataElement.UUID):
+            if size <= 1:
+                size_index = 0
+            elif size == 2:
+                size_index = 1
+            elif size == 4:
+                size_index = 2
+            elif size == 8:
+                size_index = 3
+            elif size == 16:
+                size_index = 4
+            else:
+                raise ValueError('invalid data size')
+        elif (self.type == DataElement.TEXT_STRING or
+              self.type == DataElement.SEQUENCE or
+              self.type == DataElement.ALTERNATIVE or
+              self.type == DataElement.URL):
+            if size <= 0xFF:
+                size_index = 5
+                size_bytes = bytes([size])
+            elif size <= 0xFFFF:
+                size_index = 6
+                size_bytes = struct.pack('>H', size)
+            elif size <= 0xFFFFFFFF:
+                size_index = 7
+                size_bytes = struct.pack('>I', size)
+            else:
+                raise ValueError('invalid data size')
+        elif self.type == DataElement.BOOLEAN:
+            if size != 1:
+                raise ValueError('boolean must be 1 byte')
+            size_index = 0
+
+        self.bytes = bytes([self.type << 3 | size_index]) + size_bytes + data
+        return self.bytes
+
+    def to_string(self, pretty=False, indentation=0):
+        prefix = '  ' * indentation
+        type_name = name_or_number(self.TYPE_NAMES, self.type)
+        if self.type == DataElement.NIL:
+            value_string = ''
+        elif self.type == DataElement.SEQUENCE or self.type == DataElement.ALTERNATIVE:
+            container_separator = '\n' if pretty else ''
+            element_separator = '\n' if pretty else ','
+            value_string = f'[{container_separator}{element_separator.join([element.to_string(pretty, indentation + 1 if pretty else 0) for element in self.value])}{container_separator}{prefix}]'
+        elif self.type == DataElement.UNSIGNED_INTEGER or self.type == DataElement.SIGNED_INTEGER:
+            value_string = f'{self.value}#{self.value_size}'
+        elif isinstance(self.value, DataElement):
+            value_string = self.value.to_string(pretty, indentation)
+        else:
+            value_string = str(self.value)
+        return f'{prefix}{type_name}({value_string})'
+
+    def __str__(self):
+        return self.to_string()
+
+
+# -----------------------------------------------------------------------------
+class ServiceAttribute:
+    def __init__(self, id, value):
+        self.id    = id
+        self.value = value
+
+    @staticmethod
+    def list_from_data_elements(elements):
+        attribute_list = []
+        for i in range(0, len(elements) // 2):
+            attribute_id, attribute_value = elements[2 * i:2 * (i + 1)]
+            if attribute_id.type != DataElement.UNSIGNED_INTEGER:
+                logger.warn('attribute ID element is not an integer')
+                continue
+            attribute_list.append(ServiceAttribute(attribute_id.value, attribute_value))
+
+        return attribute_list
+
+    @staticmethod
+    def find_attribute_in_list(attribute_list, attribute_id):
+        return next((attribute.value for attribute in attribute_list if attribute.id == attribute_id), None)
+
+    @staticmethod
+    def id_name(id):
+        return name_or_number(SDP_ATTRIBUTE_ID_NAMES, id)
+
+    @staticmethod
+    def is_uuid_in_value(uuid, value):
+        # Find if a uuid matches a value, either directly or recursing into sequences
+        if value.type == DataElement.UUID:
+            return value.value == uuid
+        elif value.type == DataElement.SEQUENCE:
+            for element in value.value:
+                if ServiceAttribute.is_uuid_in_value(uuid, element):
+                    return True
+            return False
+        else:
+            return False
+
+    def to_string(self, color=False):
+        if color:
+            return f'Attribute(id={colors.color(self.id_name(self.id),"magenta")},value={self.value})'
+        else:
+            return f'Attribute(id={self.id_name(self.id)},value={self.value})'
+
+    def __str__(self):
+        return self.to_string()
+
+
+# -----------------------------------------------------------------------------
+class SDP_PDU:
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.2 PROTOCOL DATA UNIT FORMAT
+    '''
+    sdp_pdu_classes = {}
+
+    @staticmethod
+    def from_bytes(pdu):
+        pdu_id, transaction_id, parameters_length = struct.unpack_from('>BHH', pdu, 0)
+
+        cls = SDP_PDU.sdp_pdu_classes.get(pdu_id)
+        if cls is None:
+            instance = SDP_PDU(pdu)
+            instance.name = SDP_PDU.pdu_name(pdu_id)
+            instance.pdu_id = pdu_id
+            instance.transaction_id = transaction_id
+            return instance
+        self = cls.__new__(cls)
+        SDP_PDU.__init__(self, pdu, transaction_id)
+        if hasattr(self, 'fields'):
+            self.init_from_bytes(pdu, 5)
+        return self
+
+    @staticmethod
+    def parse_service_record_handle_list_preceded_by_count(data, offset):
+        count = struct.unpack_from('>H', data, offset - 2)[0]
+        handle_list = [struct.unpack_from('>I', data, offset + x * 4)[0] for x in range(count)]
+        return offset + count * 4, handle_list
+
+    @staticmethod
+    def parse_bytes_preceded_by_length(data, offset):
+        length = struct.unpack_from('>H', data, offset - 2)[0]
+        return offset + length, data[offset:offset + length]
+
+    @staticmethod
+    def error_name(error_code):
+        return name_or_number(SDP_ERROR_NAMES, error_code)
+
+    @staticmethod
+    def pdu_name(code):
+        return name_or_number(SDP_PDU_NAMES, code)
+
+    @staticmethod
+    def subclass(fields):
+        def inner(cls):
+            name = cls.__name__
+
+            # add a _ character before every uppercase letter, except the SDP_ prefix
+            location = len(name) - 1
+            while location > 4:
+                if not name[location].isupper():
+                    location -= 1
+                    continue
+                name = name[:location] + '_' + name[location:]
+                location -= 1
+
+            cls.name = name.upper()
+            cls.pdu_id = key_with_value(SDP_PDU_NAMES, cls.name)
+            if cls.pdu_id is None:
+                raise KeyError(f'PDU name {cls.name} not found in SDP_PDU_NAMES')
+            cls.fields = fields
+
+            # Register a factory for this class
+            SDP_PDU.sdp_pdu_classes[cls.pdu_id] = cls
+
+            return cls
+
+        return inner
+
+    def __init__(self, pdu=None, transaction_id=0, **kwargs):
+        if hasattr(self, 'fields') and kwargs:
+            HCI_Object.init_from_fields(self, self.fields, kwargs)
+        if pdu is None:
+            parameters = HCI_Object.dict_to_bytes(kwargs, self.fields)
+            pdu = struct.pack('>BHH', self.pdu_id, transaction_id, len(parameters)) + parameters
+        self.pdu = pdu
+        self.transaction_id = transaction_id
+
+    def init_from_bytes(self, pdu, offset):
+        return HCI_Object.init_from_bytes(self, pdu, offset, self.fields)
+
+    def to_bytes(self):
+        return self.pdu
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        result = f'{color(self.name, "blue")} [TID={self.transaction_id}]'
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        elif len(self.pdu) > 1:
+            result += f': {self.pdu.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('error_code', {'size': 2, 'mapper': SDP_PDU.error_name})
+])
+class SDP_ErrorResponse(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.4.1 SDP_ErrorResponse PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('service_search_pattern',       DataElement.parse_from_bytes),
+    ('maximum_service_record_count', '>2'),
+    ('continuation_state',          '*')
+])
+class SDP_ServiceSearchRequest(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.5.1 SDP_ServiceSearchRequest PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('total_service_record_count',   '>2'),
+    ('current_service_record_count', '>2'),
+    ('service_record_handle_list',   SDP_PDU.parse_service_record_handle_list_preceded_by_count),
+    ('continuation_state',           '*')
+])
+class SDP_ServiceSearchResponse(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.5.2 SDP_ServiceSearchResponse PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('service_record_handle',        '>4'),
+    ('maximum_attribute_byte_count', '>2'),
+    ('attribute_id_list',            DataElement.parse_from_bytes),
+    ('continuation_state',           '*')
+])
+class SDP_ServiceAttributeRequest(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.6.1 SDP_ServiceAttributeRequest PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('attribute_list_byte_count', '>2'),
+    ('attribute_list',            SDP_PDU.parse_bytes_preceded_by_length),
+    ('continuation_state',        '*')
+])
+class SDP_ServiceAttributeResponse(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.6.2 SDP_ServiceAttributeResponse PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('service_search_pattern',       DataElement.parse_from_bytes),
+    ('maximum_attribute_byte_count', '>2'),
+    ('attribute_id_list',            DataElement.parse_from_bytes),
+    ('continuation_state',          '*')
+])
+class SDP_ServiceSearchAttributeRequest(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.7.1 SDP_ServiceSearchAttributeRequest PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SDP_PDU.subclass([
+    ('attribute_lists_byte_count', '>2'),
+    ('attribute_lists',            SDP_PDU.parse_bytes_preceded_by_length),
+    ('continuation_state',        '*')
+])
+class SDP_ServiceSearchAttributeResponse(SDP_PDU):
+    '''
+    See Bluetooth spec @ Vol 3, Part B - 4.7.2 SDP_ServiceSearchAttributeResponse PDU
+    '''
+
+
+# -----------------------------------------------------------------------------
+class Client:
+    def __init__(self, device):
+        self.device          = device
+        self.pending_request = None
+        self.channel         = None
+
+    async def connect(self, connection):
+        result = await self.device.l2cap_channel_manager.connect(connection, SDP_PSM)
+        self.channel = result
+
+    async def disconnect(self):
+        if self.channel:
+            await self.channel.disconnect()
+            self.channel = None
+
+    async def search_services(self, uuids):
+        if self.pending_request is not None:
+            raise InvalidStateError('request already pending')
+
+        service_search_pattern = DataElement.sequence([DataElement.uuid(uuid) for uuid in uuids])
+
+        # Request and accumulate until there's no more continuation
+        service_record_handle_list = []
+        continuation_state = bytes([0])
+        watchdog = SDP_CONTINUATION_WATCHDOG
+        while watchdog > 0:
+            response_pdu = await self.channel.send_request(
+                SDP_ServiceSearchRequest(
+                    transaction_id               = 0,  # Transaction ID TODO: pick a real value
+                    service_search_pattern       = service_search_pattern,
+                    maximum_service_record_count = 0xFFFF,
+                    continuation_state           = continuation_state
+                )
+            )
+            response = SDP_PDU.from_bytes(response_pdu)
+            logger.debug(f'<<< Response: {response}')
+            service_record_handle_list += response.service_record_handle_list
+            continuation_state = response.continuation_state
+            if len(continuation_state) == 1 and continuation_state[0] == 0:
+                break
+            logger.debug(f'continuation: {continuation_state.hex()}')
+            watchdog -= 1
+
+        return service_record_handle_list
+
+    async def search_attributes(self, uuids, attribute_ids):
+        if self.pending_request is not None:
+            raise InvalidStateError('request already pending')
+
+        service_search_pattern = DataElement.sequence([DataElement.uuid(uuid) for uuid in uuids])
+        attribute_id_list = DataElement.sequence(
+            [
+                DataElement.unsigned_integer(attribute_id[0], value_size=attribute_id[1])
+                if type(attribute_id) is tuple
+                else DataElement.unsigned_integer_16(attribute_id)
+                for attribute_id in attribute_ids
+            ]
+        )
+
+        # Request and accumulate until there's no more continuation
+        accumulator = b''
+        continuation_state = bytes([0])
+        watchdog = SDP_CONTINUATION_WATCHDOG
+        while watchdog > 0:
+            response_pdu = await self.channel.send_request(
+                SDP_ServiceSearchAttributeRequest(
+                    transaction_id               = 0,  # Transaction ID TODO: pick a real value
+                    service_search_pattern       = service_search_pattern,
+                    maximum_attribute_byte_count = 0xFFFF,
+                    attribute_id_list            = attribute_id_list,
+                    continuation_state           = continuation_state
+                )
+            )
+            response = SDP_PDU.from_bytes(response_pdu)
+            logger.debug(f'<<< Response: {response}')
+            accumulator += response.attribute_lists
+            continuation_state = response.continuation_state
+            if len(continuation_state) == 1 and continuation_state[0] == 0:
+                break
+            logger.debug(f'continuation: {continuation_state.hex()}')
+            watchdog -= 1
+
+        # Parse the result into attribute lists
+        attribute_lists_sequences = DataElement.from_bytes(accumulator)
+        if attribute_lists_sequences.type != DataElement.SEQUENCE:
+            logger.warn('unexpected data type')
+            return []
+
+        return [
+            ServiceAttribute.list_from_data_elements(sequence.value)
+            for sequence in attribute_lists_sequences.value
+            if sequence.type == DataElement.SEQUENCE
+        ]
+
+    async def get_attributes(self, service_record_handle, attribute_ids):
+        if self.pending_request is not None:
+            raise InvalidStateError('request already pending')
+
+        attribute_id_list = DataElement.sequence(
+            [
+                DataElement.unsigned_integer(attribute_id[0], value_size=attribute_id[1])
+                if type(attribute_id) is tuple
+                else DataElement.unsigned_integer_16(attribute_id)
+                for attribute_id in attribute_ids
+            ]
+        )
+
+        # Request and accumulate until there's no more continuation
+        accumulator = b''
+        continuation_state = bytes([0])
+        watchdog = SDP_CONTINUATION_WATCHDOG
+        while watchdog > 0:
+            response_pdu = await self.channel.send_request(
+                SDP_ServiceAttributeRequest(
+                    transaction_id               = 0,  # Transaction ID TODO: pick a real value
+                    service_record_handle        = service_record_handle,
+                    maximum_attribute_byte_count = 0xFFFF,
+                    attribute_id_list            = attribute_id_list,
+                    continuation_state           = continuation_state
+                )
+            )
+            response = SDP_PDU.from_bytes(response_pdu)
+            logger.debug(f'<<< Response: {response}')
+            accumulator += response.attribute_list
+            continuation_state = response.continuation_state
+            if len(continuation_state) == 1 and continuation_state[0] == 0:
+                break
+            logger.debug(f'continuation: {continuation_state.hex()}')
+            watchdog -= 1
+
+        # Parse the result into a list of attributes
+        attribute_list_sequence = DataElement.from_bytes(accumulator)
+        if attribute_list_sequence.type != DataElement.SEQUENCE:
+            logger.warn('unexpected data type')
+            return []
+
+        return ServiceAttribute.list_from_data_elements(attribute_list_sequence.value)
+
+
+# -----------------------------------------------------------------------------
+class Server:
+    CONTINUATION_STATE = bytes([0x01, 0x43])
+
+    def __init__(self, device):
+        self.device           = device
+        self.service_records  = {}  # Service records maps, by record handle
+        self.current_response = None
+
+    def register(self, l2cap_channel_manager):
+        l2cap_channel_manager.register_server(SDP_PSM, self.on_connection)
+
+    def send_response(self, response):
+        logger.debug(f'{color(">>> Sending SDP Response", "blue")}: {response}')
+        self.channel.send_pdu(response)
+
+    def match_services(self, search_pattern):
+        # Find the services for which the attributes in the pattern is a subset of the
+        # service's attribute values (NOTE: the value search recurses into sequences)
+        matching_services = {}
+        for handle, service in self.service_records.items():
+            for uuid in search_pattern.value:
+                found = False
+                for attribute in service:
+                    if ServiceAttribute.is_uuid_in_value(uuid.value, attribute.value):
+                        found = True
+                        break
+                if found:
+                    matching_services[handle] = service
+                    break
+
+        return matching_services
+
+    def on_connection(self, channel):
+        self.channel = channel
+        self.channel.sink = self.on_pdu
+
+    def on_pdu(self, pdu):
+        try:
+            sdp_pdu = SDP_PDU.from_bytes(pdu)
+        except Exception as error:
+            logger.warn(color(f'failed to parse SDP Request PDU: {error}', 'red'))
+            self.send_response(
+                SDP_ErrorResponse(
+                    transaction_id = 0,
+                    error_code     = SDP_INVALID_REQUEST_SYNTAX_ERROR
+                )
+            )
+
+        logger.debug(f'{color("<<< Received SDP Request", "green")}: {sdp_pdu}')
+
+        # Find the handler method
+        handler_name = f'on_{sdp_pdu.name.lower()}'
+        handler = getattr(self, handler_name, None)
+        if handler:
+            try:
+                handler(sdp_pdu)
+            except Exception as error:
+                logger.warning(f'{color("!!! Exception in handler:", "red")} {error}')
+                self.send_response(
+                    SDP_ErrorResponse(
+                        transaction_id = sdp_pdu.transaction_id,
+                        error_code     = SDP_INSUFFICIENT_RESOURCES_TO_SATISFY_REQUEST_ERROR
+                    )
+                )
+        else:
+            logger.error(color('SDP Request not handled???', 'red'))
+            self.send_response(
+                SDP_ErrorResponse(
+                    transaction_id = sdp_pdu.transaction_id,
+                    error_code     = SDP_INVALID_REQUEST_SYNTAX_ERROR
+                )
+            )
+
+    def get_next_response_payload(self, maximum_size):
+        if len(self.current_response) > maximum_size:
+            payload = self.current_response[:maximum_size]
+            continuation_state = Server.CONTINUATION_STATE
+            self.current_response = self.current_response[maximum_size:]
+        else:
+            payload = self.current_response
+            continuation_state = bytes([0])
+            self.current_response = None
+
+        return (payload, continuation_state)
+
+    @staticmethod
+    def get_service_attributes(service, attribute_ids):
+        attributes = []
+        for attribute_id in attribute_ids:
+            if attribute_id.value_size == 4:
+                # Attribute ID range
+                id_range_start = attribute_id.value >> 16
+                id_range_end   = attribute_id.value & 0xFFFF
+            else:
+                id_range_start = attribute_id.value
+                id_range_end   = attribute_id.value
+            attributes += [
+                attribute for attribute in service
+                if attribute.id >= id_range_start and attribute.id <= id_range_end
+            ]
+
+        # Return the maching attributes, sorted by attribute id
+        attributes.sort(key = lambda x: x.id)
+        attribute_list = DataElement.sequence([])
+        for attribute in attributes:
+            attribute_list.value.append(DataElement.unsigned_integer_16(attribute.id))
+            attribute_list.value.append(attribute.value)
+
+        return attribute_list
+
+    def on_sdp_service_search_request(self, request):
+        # Check if this is a continuation
+        if len(request.continuation_state) > 1:
+            if not self.current_response:
+                self.send_response(
+                    SDP_ErrorResponse(
+                        transaction_id = request.transaction_id,
+                        error_code     = SDP_INVALID_CONTINUATION_STATE_ERROR
+                    )
+                )
+                return
+        else:
+            # Cleanup any partial response leftover
+            self.current_response = None
+
+            # Find the matching services
+            matching_services = self.match_services(request.service_search_pattern)
+            service_record_handles = list(matching_services.keys())
+
+            # Only return up to the maximum requested
+            service_record_handles_subset = service_record_handles[:request.maximum_service_record_count]
+
+            # Serialize to a byte array, and remember the total count
+            logger.debug(f'Service Record Handles: {service_record_handles}')
+            self.current_response = (
+                len(service_record_handles),
+                service_record_handles_subset
+            )
+
+        # Respond, keeping any unsent handles for later
+        service_record_handles = self.current_response[1][:request.maximum_service_record_count]
+        self.current_response = (
+            self.current_response[0],
+            self.current_response[1][request.maximum_service_record_count:]
+        )
+        continuation_state = Server.CONTINUATION_STATE if self.current_response[1] else bytes([0])
+        service_record_handle_list = b''.join([struct.pack('>I', handle) for handle in service_record_handles])
+        self.send_response(
+            SDP_ServiceSearchResponse(
+                transaction_id               = request.transaction_id,
+                total_service_record_count   = self.current_response[0],
+                current_service_record_count = len(service_record_handles),
+                service_record_handle_list   = service_record_handle_list,
+                continuation_state           = continuation_state
+            )
+        )
+
+    def on_sdp_service_attribute_request(self, request):
+        # Check if this is a continuation
+        if len(request.continuation_state) > 1:
+            if not self.current_response:
+                self.send_response(
+                    SDP_ErrorResponse(
+                        transaction_id = request.transaction_id,
+                        error_code     = SDP_INVALID_CONTINUATION_STATE_ERROR
+                    )
+                )
+                return
+        else:
+            # Cleanup any partial response leftover
+            self.current_response = None
+
+            # Check that the service exists
+            service = self.service_records.get(request.service_record_handle)
+            if service is None:
+                self.send_response(
+                    SDP_ErrorResponse(
+                        transaction_id = request.transaction_id,
+                        error_code     = SDP_INVALID_SERVICE_RECORD_HANDLE_ERROR
+                    )
+                )
+                return
+
+            # Get the attributes for the service
+            attribute_list = Server.get_service_attributes(service, request.attribute_id_list.value)
+
+            # Serialize to a byte array
+            logger.debug(f'Attributes: {attribute_list}')
+            self.current_response = bytes(attribute_list)
+
+        # Respond, keeping any pending chunks for later
+        attribute_list, continuation_state = self.get_next_response_payload(request.maximum_attribute_byte_count)
+        self.send_response(
+            SDP_ServiceAttributeResponse(
+                transaction_id            = request.transaction_id,
+                attribute_list_byte_count = len(attribute_list),
+                attribute_list            = attribute_list,
+                continuation_state        = continuation_state
+            )
+        )
+
+    def on_sdp_service_search_attribute_request(self, request):
+        # Check if this is a continuation
+        if len(request.continuation_state) > 1:
+            if not self.current_response:
+                self.send_response(
+                    SDP_ErrorResponse(
+                        transaction_id = request.transaction_id,
+                        error_code     = SDP_INVALID_CONTINUATION_STATE_ERROR
+                    )
+                )
+        else:
+            # Cleanup any partial response leftover
+            self.current_response = None
+
+            # Find the matching services
+            matching_services = self.match_services(request.service_search_pattern).values()
+
+            # Filter the required attributes
+            attribute_lists = DataElement.sequence([])
+            for service in matching_services:
+                attribute_list = Server.get_service_attributes(service, request.attribute_id_list.value)
+                if attribute_list.value:
+                    attribute_lists.value.append(attribute_list)
+
+            # Serialize to a byte array
+            logger.debug(f'Search response: {attribute_lists}')
+            self.current_response = bytes(attribute_lists)
+
+        # Respond, keeping any pending chunks for later
+        attribute_lists, continuation_state = self.get_next_response_payload(request.maximum_attribute_byte_count)
+        self.send_response(
+            SDP_ServiceSearchAttributeResponse(
+                transaction_id             = request.transaction_id,
+                attribute_lists_byte_count = len(attribute_lists),
+                attribute_lists            = attribute_lists,
+                continuation_state         = continuation_state
+            )
+        )
diff --git a/bumble/smp.py b/bumble/smp.py
new file mode 100644
index 0000000..dbb2b7a
--- /dev/null
+++ b/bumble/smp.py
@@ -0,0 +1,1545 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# SMP - Security Manager Protocol
+#
+# See Bluetooth spec @ Vol 3, Part H
+#
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import secrets
+from pyee import EventEmitter
+from colors import color
+
+from .core import *
+from .hci import *
+from .keys import PairingKeys
+from . import crypto
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+SMP_CID = 0x06
+
+SMP_PAIRING_REQUEST_COMMAND               = 0x01
+SMP_PAIRING_RESPONSE_COMMAND              = 0x02
+SMP_PAIRING_CONFIRM_COMMAND               = 0x03
+SMP_PAIRING_RANDOM_COMMAND                = 0x04
+SMP_PAIRING_FAILED_COMMAND                = 0x05
+SMP_ENCRYPTION_INFORMATION_COMMAND        = 0x06
+SMP_MASTER_IDENTIFICATION_COMMAND         = 0x07
+SMP_IDENTITY_INFORMATION_COMMAND          = 0x08
+SMP_IDENTITY_ADDRESS_INFORMATION_COMMAND  = 0x09
+SMP_SIGNING_INFORMATION_COMMAND           = 0x0A
+SMP_SECURITY_REQUEST_COMMAND              = 0x0B
+SMP_PAIRING_PUBLIC_KEY_COMMAND            = 0x0C
+SMP_PAIRING_DHKEY_CHECK_COMMAND           = 0x0D
+SMP_PAIRING_KEYPRESS_NOTIFICATION_COMMAND = 0x0E
+
+SMP_COMMAND_NAMES = {
+    SMP_PAIRING_REQUEST_COMMAND:               'SMP_PAIRING_REQUEST_COMMAND',
+    SMP_PAIRING_RESPONSE_COMMAND:              'SMP_PAIRING_RESPONSE_COMMAND',
+    SMP_PAIRING_CONFIRM_COMMAND:               'SMP_PAIRING_CONFIRM_COMMAND',
+    SMP_PAIRING_RANDOM_COMMAND:                'SMP_PAIRING_RANDOM_COMMAND',
+    SMP_PAIRING_FAILED_COMMAND:                'SMP_PAIRING_FAILED_COMMAND',
+    SMP_ENCRYPTION_INFORMATION_COMMAND:        'SMP_ENCRYPTION_INFORMATION_COMMAND',
+    SMP_MASTER_IDENTIFICATION_COMMAND:         'SMP_MASTER_IDENTIFICATION_COMMAND',
+    SMP_IDENTITY_INFORMATION_COMMAND:          'SMP_IDENTITY_INFORMATION_COMMAND',
+    SMP_IDENTITY_ADDRESS_INFORMATION_COMMAND:  'SMP_IDENTITY_ADDRESS_INFORMATION_COMMAND',
+    SMP_SIGNING_INFORMATION_COMMAND:           'SMP_SIGNING_INFORMATION_COMMAND',
+    SMP_SECURITY_REQUEST_COMMAND:              'SMP_SECURITY_REQUEST_COMMAND',
+    SMP_PAIRING_PUBLIC_KEY_COMMAND:            'SMP_PAIRING_PUBLIC_KEY_COMMAND',
+    SMP_PAIRING_DHKEY_CHECK_COMMAND:           'SMP_PAIRING_DHKEY_CHECK_COMMAND',
+    SMP_PAIRING_KEYPRESS_NOTIFICATION_COMMAND: 'SMP_PAIRING_KEYPRESS_NOTIFICATION_COMMAND'
+}
+
+SMP_DISPLAY_ONLY_IO_CAPABILITY       = 0x00
+SMP_DISPLAY_YES_NO_IO_CAPABILITY     = 0x01
+SMP_KEYBOARD_ONLY_IO_CAPABILITY      = 0x02
+SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY = 0x03
+SMP_KEYBOARD_DISPLAY_IO_CAPABILITY   = 0x04
+
+SMP_IO_CAPABILITY_NAMES = {
+    SMP_DISPLAY_ONLY_IO_CAPABILITY:       'SMP_DISPLAY_ONLY_IO_CAPABILITY',
+    SMP_DISPLAY_YES_NO_IO_CAPABILITY:     'SMP_DISPLAY_YES_NO_IO_CAPABILITY',
+    SMP_KEYBOARD_ONLY_IO_CAPABILITY:      'SMP_KEYBOARD_ONLY_IO_CAPABILITY',
+    SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: 'SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY',
+    SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   'SMP_KEYBOARD_DISPLAY_IO_CAPABILITY'
+}
+
+SMP_PASSKEY_ENTRY_FAILED_ERROR                       = 0x01
+SMP_OOB_NOT_AVAILABLE_ERROR                          = 0x02
+SMP_AUTHENTICATION_REQUIREMENTS_ERROR                = 0x03
+SMP_CONFIRM_VALUE_FAILED_ERROR                       = 0x04
+SMP_PAIRING_NOT_SUPPORTED_ERROR                      = 0x05
+SMP_ENCRYPTION_KEY_SIZE_ERROR                        = 0x06
+SMP_COMMAND_NOT_SUPPORTED_ERROR                      = 0x07
+SMP_UNSPECIFIED_REASON_ERROR                         = 0x08
+SMP_REPEATED_ATTEMPTS_ERROR                          = 0x09
+SMP_INVALID_PARAMETERS_ERROR                         = 0x0A
+SMP_DHKEY_CHECK_FAILED_ERROR                         = 0x0B
+SMP_NUMERIC_COMPARISON_FAILED_ERROR                  = 0x0C
+SMP_BD_EDR_PAIRING_IN_PROGRESS_ERROR                 = 0x0D
+SMP_CROSS_TRANSPORT_KEY_DERIVATION_NOT_ALLOWED_ERROR = 0x0E
+
+SMP_ERROR_NAMES = {
+    SMP_PASSKEY_ENTRY_FAILED_ERROR:                       'SMP_PASSKEY_ENTRY_FAILED_ERROR',
+    SMP_OOB_NOT_AVAILABLE_ERROR:                          'SMP_OOB_NOT_AVAILABLE_ERROR',
+    SMP_AUTHENTICATION_REQUIREMENTS_ERROR:                'SMP_AUTHENTICATION_REQUIREMENTS_ERROR',
+    SMP_CONFIRM_VALUE_FAILED_ERROR:                       'SMP_CONFIRM_VALUE_FAILED_ERROR',
+    SMP_PAIRING_NOT_SUPPORTED_ERROR:                      'SMP_PAIRING_NOT_SUPPORTED_ERROR',
+    SMP_ENCRYPTION_KEY_SIZE_ERROR:                        'SMP_ENCRYPTION_KEY_SIZE_ERROR',
+    SMP_COMMAND_NOT_SUPPORTED_ERROR:                      'SMP_COMMAND_NOT_SUPPORTED_ERROR',
+    SMP_UNSPECIFIED_REASON_ERROR:                         'SMP_UNSPECIFIED_REASON_ERROR',
+    SMP_REPEATED_ATTEMPTS_ERROR:                          'SMP_REPEATED_ATTEMPTS_ERROR',
+    SMP_INVALID_PARAMETERS_ERROR:                         'SMP_INVALID_PARAMETERS_ERROR',
+    SMP_DHKEY_CHECK_FAILED_ERROR:                         'SMP_DHKEY_CHECK_FAILED_ERROR',
+    SMP_NUMERIC_COMPARISON_FAILED_ERROR:                  'SMP_NUMERIC_COMPARISON_FAILED_ERROR',
+    SMP_BD_EDR_PAIRING_IN_PROGRESS_ERROR:                 'SMP_BD_EDR_PAIRING_IN_PROGRESS_ERROR',
+    SMP_CROSS_TRANSPORT_KEY_DERIVATION_NOT_ALLOWED_ERROR: 'SMP_CROSS_TRANSPORT_KEY_DERIVATION_NOT_ALLOWED_ERROR'
+}
+
+SMP_PASSKEY_ENTRY_STARTED_KEYPRESS_NOTIFICATION_TYPE   = 0
+SMP_PASSKEY_DIGIT_ENTERED_KEYPRESS_NOTIFICATION_TYPE   = 1
+SMP_PASSKEY_DIGIT_ERASED_KEYPRESS_NOTIFICATION_TYPE    = 2
+SMP_PASSKEY_CLEARED_KEYPRESS_NOTIFICATION_TYPE         = 3
+SMP_PASSKEY_ENTRY_COMPLETED_KEYPRESS_NOTIFICATION_TYPE = 4
+
+SMP_KEYPRESS_NOTIFICATION_TYPE_NAMES = {
+    SMP_PASSKEY_ENTRY_STARTED_KEYPRESS_NOTIFICATION_TYPE:   'SMP_PASSKEY_ENTRY_STARTED_KEYPRESS_NOTIFICATION_TYPE',
+    SMP_PASSKEY_DIGIT_ENTERED_KEYPRESS_NOTIFICATION_TYPE:   'SMP_PASSKEY_DIGIT_ENTERED_KEYPRESS_NOTIFICATION_TYPE',
+    SMP_PASSKEY_DIGIT_ERASED_KEYPRESS_NOTIFICATION_TYPE:    'SMP_PASSKEY_DIGIT_ERASED_KEYPRESS_NOTIFICATION_TYPE',
+    SMP_PASSKEY_CLEARED_KEYPRESS_NOTIFICATION_TYPE:         'SMP_PASSKEY_CLEARED_KEYPRESS_NOTIFICATION_TYPE',
+    SMP_PASSKEY_ENTRY_COMPLETED_KEYPRESS_NOTIFICATION_TYPE: 'SMP_PASSKEY_ENTRY_COMPLETED_KEYPRESS_NOTIFICATION_TYPE'
+}
+
+# Bit flags for key distribution/generation
+SMP_ENC_KEY_DISTRIBUTION_FLAG  = 0b0001
+SMP_ID_KEY_DISTRIBUTION_FLAG   = 0b0010
+SMP_SIGN_KEY_DISTRIBUTION_FLAG = 0b0100
+SMP_LINK_KEY_DISTRIBUTION_FLAG = 0b1000
+
+# AuthReq fields
+SMP_BONDING_AUTHREQ  = 0b00000001
+SMP_MITM_AUTHREQ     = 0b00000100
+SMP_SC_AUTHREQ       = 0b00001000
+SMP_KEYPRESS_AUTHREQ = 0b00010000
+SMP_CT2_AUTHREQ      = 0b00100000
+
+# Crypto salt
+SMP_CTKD_H7_LEBR_SALT = bytes.fromhex('00000000000000000000000000000000746D7031')
+
+# -----------------------------------------------------------------------------
+# Utils
+# -----------------------------------------------------------------------------
+def error_name(error_code):
+    return name_or_number(SMP_ERROR_NAMES, error_code)
+
+
+# -----------------------------------------------------------------------------
+# Classes
+# -----------------------------------------------------------------------------
+class SMP_Command:
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3 SECURITY MANAGER PROTOCOL
+    '''
+    smp_classes = {}
+    code = 0
+
+    @staticmethod
+    def from_bytes(pdu):
+        code = pdu[0]
+
+        cls = SMP_Command.smp_classes.get(code)
+        if cls is None:
+            instance = SMP_Command(pdu)
+            instance.name = SMP_Command.command_name(code)
+            instance.code = code
+            return instance
+        self = cls.__new__(cls)
+        SMP_Command.__init__(self, pdu)
+        if hasattr(self, 'fields'):
+            self.init_from_bytes(pdu, 1)
+        return self
+
+    @staticmethod
+    def command_name(code):
+        return name_or_number(SMP_COMMAND_NAMES, code)
+
+    @staticmethod
+    def auth_req_str(value):
+        bonding_flags = value & 3
+        mitm          = (value >> 2) & 1
+        sc            = (value >> 3) & 1
+        keypress      = (value >> 4) & 1
+        ct2           = (value >> 5) & 1
+
+        return f'bonding_flags={bonding_flags}, MITM={mitm}, sc={sc}, keypress={keypress}, ct2={ct2}'
+
+    @staticmethod
+    def io_capability_name(io_capability):
+        return name_or_number(SMP_IO_CAPABILITY_NAMES, io_capability)
+
+    @staticmethod
+    def key_distribution_str(value):
+        key_types = []
+        if value & SMP_ENC_KEY_DISTRIBUTION_FLAG:
+            key_types.append('ENC')
+        if value & SMP_ID_KEY_DISTRIBUTION_FLAG:
+            key_types.append('ID')
+        if value & SMP_SIGN_KEY_DISTRIBUTION_FLAG:
+            key_types.append('SIGN')
+        if value & SMP_LINK_KEY_DISTRIBUTION_FLAG:
+            key_types.append('LINK')
+        return ','.join(key_types)
+
+    @staticmethod
+    def keypress_notification_type_name(notification_type):
+        return name_or_number(SMP_KEYPRESS_NOTIFICATION_TYPE_NAMES, notification_type)
+
+    @staticmethod
+    def subclass(fields):
+        def inner(cls):
+            cls.name = cls.__name__.upper()
+            cls.code = key_with_value(SMP_COMMAND_NAMES, cls.name)
+            if cls.code is None:
+                raise KeyError(f'Command name {cls.name} not found in SMP_COMMAND_NAMES')
+            cls.fields = fields
+
+            # Register a factory for this class
+            SMP_Command.smp_classes[cls.code] = cls
+
+            return cls
+
+        return inner
+
+    def __init__(self, pdu=None, **kwargs):
+        if hasattr(self, 'fields') and kwargs:
+            HCI_Object.init_from_fields(self, self.fields, kwargs)
+        if pdu is None:
+            pdu = bytes([self.code]) + HCI_Object.dict_to_bytes(kwargs, self.fields)
+        self.pdu = pdu
+
+    def init_from_bytes(self, pdu, offset):
+        return HCI_Object.init_from_bytes(self, pdu, offset, self.fields)
+
+    def to_bytes(self):
+        return self.pdu
+
+    def __bytes__(self):
+        return self.to_bytes()
+
+    def __str__(self):
+        result = color(self.name, 'yellow')
+        if fields := getattr(self, 'fields', None):
+            result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, '  ')
+        else:
+            if len(self.pdu) > 1:
+                result += f': {self.pdu.hex()}'
+        return result
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('io_capability',               {'size': 1, 'mapper': SMP_Command.io_capability_name}),
+    ('oob_data_flag',               1),
+    ('auth_req',                    {'size': 1, 'mapper': SMP_Command.auth_req_str}),
+    ('maximum_encryption_key_size', 1),
+    ('initiator_key_distribution',  {'size': 1, 'mapper': SMP_Command.key_distribution_str}),
+    ('responder_key_distribution',  {'size': 1, 'mapper': SMP_Command.key_distribution_str})
+])
+class SMP_Pairing_Request_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.1 Pairing Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('io_capability',               {'size': 1, 'mapper': SMP_Command.io_capability_name}),
+    ('oob_data_flag',               1),
+    ('auth_req',                    {'size': 1, 'mapper': SMP_Command.auth_req_str}),
+    ('maximum_encryption_key_size', 1),
+    ('initiator_key_distribution',  {'size': 1, 'mapper': SMP_Command.key_distribution_str}),
+    ('responder_key_distribution',  {'size': 1, 'mapper': SMP_Command.key_distribution_str})
+])
+class SMP_Pairing_Response_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.2 Pairing Response
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('confirm_value', 16)
+])
+class SMP_Pairing_Confirm_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.3 Pairing Confirm
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('random_value', 16)
+])
+class SMP_Pairing_Random_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.4 Pairing Random
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('reason', {'size': 1, 'mapper': error_name})
+])
+class SMP_Pairing_Failed_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.5 Pairing Failed
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('public_key_x', 32),
+    ('public_key_y', 32)
+])
+class SMP_Pairing_Public_Key_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.6 Pairing Public Key
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('dhkey_check', 16),
+])
+class SMP_Pairing_DHKey_Check_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.7 Pairing DHKey Check
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('notification_type', {'size': 1, 'mapper': SMP_Command.keypress_notification_type_name}),
+])
+class SMP_Pairing_Keypress_Notification_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.5.8 Keypress Notification
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('long_term_key', 16)
+])
+class SMP_Encryption_Information_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.6.2 Encryption Information
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('ediv', 2),
+    ('rand', 8)
+])
+class SMP_Master_Identification_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.6.3 Master Identification
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('identity_resolving_key', 16)
+])
+class SMP_Identity_Information_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.6.4 Identity Information
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('addr_type', Address.ADDRESS_TYPE_SPEC),
+    ('bd_addr',   Address.parse_address_preceded_by_type)
+])
+class SMP_Identity_Address_Information_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.6.5 Identity Address Information
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('signature_key', 16)
+])
+class SMP_Signing_Information_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.6.6 Signing Information
+    '''
+
+
+# -----------------------------------------------------------------------------
+@SMP_Command.subclass([
+    ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}),
+])
+class SMP_Security_Request_Command(SMP_Command):
+    '''
+    See Bluetooth spec @ Vol 3, Part H - 3.6.7 Security Request
+    '''
+
+
+# -----------------------------------------------------------------------------
+def smp_auth_req(bonding, mitm, sc, keypress, ct2):
+    value = 0
+    if bonding:
+        value |= SMP_BONDING_AUTHREQ
+    if mitm:
+        value |= SMP_MITM_AUTHREQ
+    if sc:
+        value |= SMP_SC_AUTHREQ
+    if keypress:
+        value |= SMP_KEYPRESS_AUTHREQ
+    if ct2:
+        value |= SMP_CT2_AUTHREQ
+    return value
+
+
+# -----------------------------------------------------------------------------
+class AddressResolver:
+    def __init__(self, resolving_keys):
+        self.resolving_keys = resolving_keys
+
+    def resolve(self, address):
+        address_bytes = bytes(address)
+        hash = address_bytes[0:3]
+        prand = address_bytes[3:6]
+        for (irk, resolved_address) in self.resolving_keys:
+            local_hash = crypto.ah(irk, prand)
+            if local_hash == hash:
+                # Match!
+                if resolved_address.address_type == Address.PUBLIC_DEVICE_ADDRESS:
+                    resolved_address_type = Address.PUBLIC_IDENTITY_ADDRESS
+                else:
+                    resolved_address_type = Address.RANDOM_IDENTITY_ADDRESS
+                return Address(address=str(resolved_address), address_type=resolved_address_type)
+
+
+# -----------------------------------------------------------------------------
+class PairingDelegate:
+    NO_OUTPUT_NO_INPUT                = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
+    KEYBOARD_INPUT_ONLY               = SMP_KEYBOARD_ONLY_IO_CAPABILITY
+    DISPLAY_OUTPUT_ONLY               = SMP_DISPLAY_ONLY_IO_CAPABILITY
+    DISPLAY_OUTPUT_AND_YES_NO_INPUT   = SMP_DISPLAY_YES_NO_IO_CAPABILITY
+    DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY
+    DEFAULT_KEY_DISTRIBUTION          = (SMP_ENC_KEY_DISTRIBUTION_FLAG | SMP_ID_KEY_DISTRIBUTION_FLAG)
+
+    def __init__(
+        self,
+        io_capability=NO_OUTPUT_NO_INPUT,
+        local_initiator_key_distribution=DEFAULT_KEY_DISTRIBUTION,
+        local_responder_key_distribution=DEFAULT_KEY_DISTRIBUTION
+    ):
+        self.io_capability = io_capability
+        self.local_initiator_key_distribution = local_initiator_key_distribution
+        self.local_responder_key_distribution = local_responder_key_distribution
+
+    async def accept(self):
+        return True
+
+    async def compare_numbers(self, number, digits=6):
+        return True
+
+    async def get_number(self):
+        return 0
+
+    async def display_number(self, number, digits=6):
+        pass
+
+    async def key_distribution_response(self, peer_initiator_key_distribution, peer_responder_key_distribution):
+        return (
+            (peer_initiator_key_distribution &
+             self.local_initiator_key_distribution),
+            (peer_responder_key_distribution &
+             self.local_responder_key_distribution)
+        )
+
+
+# -----------------------------------------------------------------------------
+class PairingConfig:
+    def __init__(self, sc=True, mitm=True, bonding=True, delegate=None):
+        self.sc       = sc
+        self.mitm     = mitm
+        self.bonding  = bonding
+        self.delegate = delegate or PairingDelegate()
+
+    def __str__(self):
+        io_capability_str = SMP_Command.io_capability_name(self.delegate.io_capability)
+        return f'PairingConfig(sc={self.sc}, mitm={self.mitm}, bonding={self.bonding}, delegate[{io_capability_str}])'
+
+
+# -----------------------------------------------------------------------------
+class Session:
+    # Pairing methods
+    JUST_WORKS         = 0
+    NUMERIC_COMPARISON = 1
+    PASSKEY            = 2
+    OOB                = 3
+
+    PAIRING_METHOD_NAMES = {
+        JUST_WORKS:         'JUST_WORKS',
+        NUMERIC_COMPARISON: 'NUMERIC_COMPARISON',
+        PASSKEY:            'PASSKEY',
+        OOB:                'OOB'
+    }
+
+    # I/O Capability to pairing method decision matrix
+    #
+    # See Bluetooth spec @ Vol 3, part H - Table 2.8: Mapping of IO Capabilities to Key Generation Method
+    #
+    # Map: initiator -> responder -> <method>
+    # where <method> may be a simple entry or a 2-element tuple, with the first element for legacy
+    # pairing and the second  for secure connections, when the two are different.
+    # Each entry is either a method name, or, for PASSKEY, a tuple:
+    # (method, initiator_displays, responder_displays)
+    # to specify if the initiator and responder should display (True) or input a code (False).
+    PAIRING_METHODS = {
+        SMP_DISPLAY_ONLY_IO_CAPABILITY: {
+            SMP_DISPLAY_ONLY_IO_CAPABILITY:       JUST_WORKS,
+            SMP_DISPLAY_YES_NO_IO_CAPABILITY:     JUST_WORKS,
+            SMP_KEYBOARD_ONLY_IO_CAPABILITY:      (PASSKEY, True, False),
+            SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS,
+            SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   (PASSKEY, True, False),
+        },
+        SMP_DISPLAY_YES_NO_IO_CAPABILITY: {
+            SMP_DISPLAY_ONLY_IO_CAPABILITY:       JUST_WORKS,
+            SMP_DISPLAY_YES_NO_IO_CAPABILITY:     (JUST_WORKS, NUMERIC_COMPARISON),
+            SMP_KEYBOARD_ONLY_IO_CAPABILITY:      (PASSKEY, True, False),
+            SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS,
+            SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   ((PASSKEY, True, False), NUMERIC_COMPARISON)
+        },
+        SMP_KEYBOARD_ONLY_IO_CAPABILITY: {
+            SMP_DISPLAY_ONLY_IO_CAPABILITY:       (PASSKEY, False, True),
+            SMP_DISPLAY_YES_NO_IO_CAPABILITY:     (PASSKEY, False, True),
+            SMP_KEYBOARD_ONLY_IO_CAPABILITY:      (PASSKEY, False, False),
+            SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS,
+            SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   (PASSKEY, False, True),
+        },
+        SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: {
+            SMP_DISPLAY_ONLY_IO_CAPABILITY:       JUST_WORKS,
+            SMP_DISPLAY_YES_NO_IO_CAPABILITY:     JUST_WORKS,
+            SMP_KEYBOARD_ONLY_IO_CAPABILITY:      JUST_WORKS,
+            SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS,
+            SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   JUST_WORKS
+        },
+        SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: {
+            SMP_DISPLAY_ONLY_IO_CAPABILITY:       (PASSKEY, False, True),
+            SMP_DISPLAY_YES_NO_IO_CAPABILITY:     ((PASSKEY, False, True), NUMERIC_COMPARISON),
+            SMP_KEYBOARD_ONLY_IO_CAPABILITY:      (PASSKEY, True, False),
+            SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS,
+            SMP_KEYBOARD_DISPLAY_IO_CAPABILITY:   ((PASSKEY, True, False), NUMERIC_COMPARISON)
+        }
+    }
+
+    def __init__(self, manager, connection, pairing_config):
+        self.manager                     = manager
+        self.connection                  = connection
+        self.tk                          = bytes(16)
+        self.r                           = bytes(16)
+        self.stk                         = None
+        self.ltk                         = None
+        self.ltk_ediv                    = 0
+        self.ltk_rand                    = bytes(8)
+        self.link_key                    = None
+        self.initiator_key_distribution  = 0
+        self.responder_key_distribution  = 0
+        self.peer_random_value           = None
+        self.peer_public_key_x           = bytes(32)
+        self.peer_public_key_y           = bytes(32)
+        self.peer_ltk                    = None
+        self.peer_ediv                   = None
+        self.peer_rand                   = None
+        self.peer_identity_resolving_key = None
+        self.peer_bd_addr                = None
+        self.peer_signature_key          = None
+        self.peer_expected_distributions = []
+        self.dh_key                      = None
+        self.passkey                     = 0
+        self.passkey_step                = 0
+        self.passkey_display             = False
+        self.pairing_method              = 0
+        self.pairing_config              = pairing_config
+        self.wait_before_continuing      = None
+        self.completed                   = False
+
+        # Decide if we're the initiator or the responder
+        self.is_initiator = (connection.role == BT_CENTRAL_ROLE)
+        self.is_responder = not self.is_initiator
+
+        # Listen for connection events
+        connection.on('disconnection', self.on_disconnection)
+        connection.on('connection_encryption_change', self.on_connection_encryption_change)
+        connection.on('connection_encryption_key_refresh', self.on_connection_encryption_key_refresh)
+
+        # Create a future that can be used to wait for the session to complete
+        if self.is_initiator:
+            self.pairing_result = asyncio.get_running_loop().create_future()
+        else:
+            self.pairing_result = None
+
+        # Key Distribution (default values before negotiation)
+        self.initiator_key_distribution = pairing_config.delegate.local_initiator_key_distribution
+        self.responder_key_distribution = pairing_config.delegate.local_responder_key_distribution
+
+        # Authentication Requirements Flags - Vol 3, Part H, Figure 3.3
+        self.bonding  = pairing_config.bonding
+        self.sc       = pairing_config.sc
+        self.mitm     = pairing_config.mitm
+        self.keypress = False
+        self.ct2      = False
+
+        # I/O Capabilities
+        self.io_capability      = pairing_config.delegate.io_capability
+        self.peer_io_capability = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
+
+        # OOB (not supported yet)
+        self.oob = False
+
+        # Set up addresses
+        peer_address = connection.peer_resolvable_address or connection.peer_address
+        if self.is_initiator:
+            self.ia  = bytes(manager.address)
+            self.iat = 1 if manager.address.is_random else 0
+            self.ra  = bytes(peer_address)
+            self.rat = 1 if peer_address.is_random else 0
+        else:
+            self.ra  = bytes(manager.address)
+            self.rat = 1 if manager.address.is_random else 0
+            self.ia  = bytes(peer_address)
+            self.iat = 1 if peer_address.is_random else 0
+
+    @property
+    def pkx(self):
+        return (
+            bytes(reversed(self.manager.ecc_key.x)),
+            self.peer_public_key_x
+        )
+
+    @property
+    def pka(self):
+        return self.pkx[0 if self.is_initiator else 1]
+
+    @property
+    def pkb(self):
+        return self.pkx[0 if self.is_responder else 1]
+
+    @property
+    def nx(self):
+        return (
+            self.r,
+            self.peer_random_value
+        )
+
+    @property
+    def na(self):
+        return self.nx[0 if self.is_initiator else 1]
+
+    @property
+    def nb(self):
+        return self.nx[0 if self.is_responder else 1]
+
+    @property
+    def auth_req(self):
+        return smp_auth_req(self.bonding, self.mitm, self.sc, self.keypress, self.ct2)
+
+    def get_long_term_key(self, rand, ediv):
+        if not self.sc and not self.completed:
+            if rand == self.ltk_rand and ediv == self.ltk_ediv:
+                return self.stk
+        else:
+            return self.ltk
+
+    def decide_pairing_method(self, auth_req, initiator_io_capability, responder_io_capability):
+        if (not self.mitm) and (auth_req & SMP_MITM_AUTHREQ == 0):
+            self.pairing_method = self.JUST_WORKS
+            return
+
+        details = self.PAIRING_METHODS[initiator_io_capability][responder_io_capability]
+        if type(details) is tuple and len(details) == 2:
+            # One entry for legacy pairing and one for secure connections
+            details = details[1 if self.sc else 0]
+        if type(details) is int:
+            # Just a method ID
+            self.pairing_method = details
+        else:
+            # PASSKEY method, with a method ID and display/input flags
+            self.pairing_method  = details[0]
+            self.passkey_display = details[1 if self.is_initiator else 2]
+
+    def check_expected_value(self, expected, received, error):
+        logger.debug(f'expected={expected.hex()} got={received.hex()}')
+        if expected != received:
+            logger.info(color('pairing confirm/check mismatch', 'red'))
+            self.send_pairing_failed(error)
+            return False
+        return True
+
+    def prompt_user_for_numeric_comparison(self, code, next_steps):
+        async def prompt():
+            logger.debug(f'verification code: {code}')
+            try:
+                response = await self.pairing_config.delegate.compare_numbers(code, digits=6)
+                if response:
+                    next_steps()
+                    return
+            except Exception as error:
+                logger.warn(f'exception while prompting: {error}')
+
+            self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
+
+        asyncio.create_task(prompt())
+
+    def prompt_user_for_number(self, next_steps):
+        async def prompt():
+            logger.debug('prompting user for passkey')
+            try:
+                passkey = await self.pairing_config.delegate.get_number()
+                logger.debug(f'user input: {passkey}')
+                next_steps(passkey)
+            except Exception as error:
+                logger.warn(f'exception while prompting: {error}')
+                self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR)
+
+        asyncio.create_task(prompt())
+
+    def display_passkey(self):
+        # Generate random Passkey/PIN code
+        self.passkey = secrets.randbelow(1000000)
+        logger.debug(f'Pairing PIN CODE: {self.passkey:06}')
+
+        # The value of TK is computed from the PIN code
+        if not self.sc:
+            self.tk = self.passkey.to_bytes(16, byteorder='little')
+            logger.debug(f'TK from passkey = {self.tk.hex()}')
+
+        asyncio.create_task(self.pairing_config.delegate.display_number(self.passkey, digits=6))
+
+    def input_passkey(self, next_steps=None):
+        # Prompt the user for the passkey displayed on the peer
+        def after_input(passkey):
+            self.passkey = passkey
+
+            if not self.sc:
+                self.tk = passkey.to_bytes(16, byteorder='little')
+                logger.debug(f'TK from passkey = {self.tk.hex()}')
+
+            if next_steps is not None:
+                next_steps()
+        self.prompt_user_for_number(after_input)
+
+    def display_or_input_passkey(self, next_steps=None):
+        if self.passkey_display:
+            self.display_passkey()
+            if next_steps is not None:
+                next_steps()
+        else:
+            self.input_passkey(next_steps)
+
+    def send_command(self, command):
+        self.manager.send_command(self.connection, command)
+
+    def send_pairing_failed(self, error):
+        self.send_command(SMP_Pairing_Failed_Command(reason = error))
+        self.on_pairing_failure(error)
+
+    def send_pairing_request_command(self):
+        self.manager.on_session_start(self)
+
+        command = SMP_Pairing_Request_Command(
+            io_capability               = self.io_capability,
+            oob_data_flag               = 0,
+            auth_req                    = self.auth_req,
+            maximum_encryption_key_size = 16,
+            initiator_key_distribution  = self.initiator_key_distribution,
+            responder_key_distribution  = self.responder_key_distribution
+        )
+        self.preq = bytes(command)
+        self.send_command(command)
+
+    def send_pairing_response_command(self):
+        response = SMP_Pairing_Response_Command(
+            io_capability               = self.io_capability,
+            oob_data_flag               = 0,
+            auth_req                    = self.auth_req,
+            maximum_encryption_key_size = 16,
+            initiator_key_distribution  = self.initiator_key_distribution,
+            responder_key_distribution  = self.responder_key_distribution
+        )
+        self.pres = bytes(response)
+        self.send_command(response)
+
+    def send_pairing_confirm_command(self):
+        self.r = crypto.r()
+        logger.debug(f'generated random: {self.r.hex()}')
+
+        if self.sc:
+            if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+                z = 0
+            elif self.pairing_method == self.PASSKEY:
+                z = 0x80 + ((self.passkey >> self.passkey_step) & 1)
+            else:
+                return
+
+            if self.is_initiator:
+                confirm_value = crypto.f4(
+                    self.pka,
+                    self.pkb,
+                    self.r,
+                    bytes([z])
+                )
+            else:
+                confirm_value = crypto.f4(
+                    self.pkb,
+                    self.pka,
+                    self.r,
+                    bytes([z])
+                )
+        else:
+            confirm_value = crypto.c1(
+                self.tk,
+                self.r,
+                self.preq,
+                self.pres,
+                self.iat,
+                self.rat,
+                self.ia,
+                self.ra
+            )
+
+        self.send_command(SMP_Pairing_Confirm_Command(confirm_value = confirm_value))
+
+    def send_pairing_random_command(self):
+        self.send_command(SMP_Pairing_Random_Command(random_value = self.r))
+
+    def send_public_key_command(self):
+        self.send_command(
+            SMP_Pairing_Public_Key_Command(
+                public_key_x = bytes(reversed(self.manager.ecc_key.x)),
+                public_key_y = bytes(reversed(self.manager.ecc_key.y))
+            )
+        )
+
+    def send_pairing_dhkey_check_command(self):
+        self.send_command(
+            SMP_Pairing_DHKey_Check_Command(
+                dhkey_check = self.ea if self.is_initiator else self.eb
+            )
+        )
+
+    def start_encryption(self, key):
+        # We can now encrypt the connection with the short term key, so that we can
+        # distribute the long term and/or other keys over an encrypted connection
+        asyncio.create_task(
+            self.manager.device.host.send_command(
+                HCI_LE_Start_Encryption_Command(
+                    connection_handle     = self.connection.handle,
+                    random_number         = bytes(8),
+                    encrypted_diversifier = 0,
+                    long_term_key         = key
+                )
+            )
+        )
+
+    def distribute_keys(self):
+        # Distribute the keys as required
+        if self.is_initiator:
+            if not self.sc:
+                # Distribute the LTK, EDIV and RAND
+                if self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG:
+                    self.send_command(SMP_Encryption_Information_Command(long_term_key=self.ltk))
+                    self.send_command(SMP_Master_Identification_Command(ediv=self.ltk_ediv, rand=self.ltk_rand))
+
+            # Distribute IRK & BD ADDR
+            if self.initiator_key_distribution & SMP_ID_KEY_DISTRIBUTION_FLAG:
+                self.send_command(
+                    SMP_Identity_Information_Command(identity_resolving_key=self.manager.device.irk)
+                )
+                self.send_command(SMP_Identity_Address_Information_Command(
+                    addr_type = self.manager.address.address_type,
+                    bd_addr   = self.manager.address
+                ))
+
+            # Distribute CSRK
+            csrk = bytes(16)  # FIXME: testing
+            if self.initiator_key_distribution & SMP_SIGN_KEY_DISTRIBUTION_FLAG:
+                self.send_command(SMP_Signing_Information_Command(signature_key=csrk))
+            
+            # CTKD, calculate BR/EDR link key
+            if self.initiator_key_distribution & SMP_LINK_KEY_DISTRIBUTION_FLAG:
+                ilk = crypto.h7(
+                    salt=SMP_CTKD_H7_LEBR_SALT,
+                    w=self.ltk) if self.ct2 else crypto.h6(self.ltk, b'tmp1')
+                self.link_key = crypto.h6(ilk, b'lebr')
+
+        else:
+            # Distribute the LTK, EDIV and RAND
+            if not self.sc:
+                if self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG:
+                    self.send_command(SMP_Encryption_Information_Command(long_term_key=self.ltk))
+                    self.send_command(SMP_Master_Identification_Command(ediv=self.ltk_ediv, rand=self.ltk_rand))
+
+            # Distribute IRK & BD ADDR
+            if self.responder_key_distribution & SMP_ID_KEY_DISTRIBUTION_FLAG:
+                self.send_command(
+                    SMP_Identity_Information_Command(identity_resolving_key=self.manager.device.irk)
+                )
+                self.send_command(SMP_Identity_Address_Information_Command(
+                    addr_type = self.manager.address.address_type,
+                    bd_addr   = self.manager.address
+                ))
+
+            # Distribute CSRK
+            csrk = bytes(16)  # FIXME: testing
+            if self.responder_key_distribution & SMP_SIGN_KEY_DISTRIBUTION_FLAG:
+                self.send_command(SMP_Signing_Information_Command(signature_key=csrk))
+            
+            # CTKD, calculate BR/EDR link key
+            if self.responder_key_distribution & SMP_LINK_KEY_DISTRIBUTION_FLAG:
+                ilk = crypto.h7(
+                    salt=SMP_CTKD_H7_LEBR_SALT,
+                    w=self.ltk) if self.ct2 else crypto.h6(self.ltk, b'tmp1')
+                self.link_key = crypto.h6(ilk, b'lebr')
+
+    def compute_peer_expected_distributions(self, key_distribution_flags):
+        # Set our expectations for what to wait for in the key distribution phase
+        self.peer_expected_distributions = []
+        if not self.sc:
+            if (key_distribution_flags & SMP_ENC_KEY_DISTRIBUTION_FLAG != 0):
+                self.peer_expected_distributions.append(SMP_Encryption_Information_Command)
+                self.peer_expected_distributions.append(SMP_Master_Identification_Command)
+        if (key_distribution_flags & SMP_ID_KEY_DISTRIBUTION_FLAG != 0):
+            self.peer_expected_distributions.append(SMP_Identity_Information_Command)
+            self.peer_expected_distributions.append(SMP_Identity_Address_Information_Command)
+        if (key_distribution_flags & SMP_SIGN_KEY_DISTRIBUTION_FLAG != 0):
+            self.peer_expected_distributions.append(SMP_Signing_Information_Command)
+        logger.debug(f'expecting distributions: {[c.__name__ for c in self.peer_expected_distributions]}')
+
+    def check_key_distribution(self, command_class):
+        # First, check that the connection is encrypted
+        if not self.connection.is_encrypted:
+            logger.warn(color('received key distribution on a non-encrypted connection', 'red'))
+            self.send_pairing_failed(SMP_UNSPECIFIED_REASON_ERROR)
+            return
+
+        # Check that this command class is expected
+        if command_class in self.peer_expected_distributions:
+            self.peer_expected_distributions.remove(command_class)
+            logger.debug(f'remaining distributions: {[c.__name__ for c in self.peer_expected_distributions]}')
+            if not self.peer_expected_distributions:
+                # The initiator can now send its keys
+                if self.is_initiator:
+                    self.distribute_keys()
+
+                # Nothing left to expect, we're done
+                self.on_pairing()
+        else:
+            logger.warn(color(f'!!! unexpected key distribution command: {command_class.__name__}', 'red'))
+            self.send_pairing_failed(SMP_UNSPECIFIED_REASON_ERROR)
+
+    async def pair(self):
+        # Start pairing as an initiator
+        # TODO: check that this session isn't already active
+
+        # Send the pairing request to start the process
+        self.send_pairing_request_command()
+
+        # Wait for the pairing process to finish
+        await self.pairing_result
+
+    def on_disconnection(self, reason):
+        self.connection.remove_listener('disconnection', self.on_disconnection)
+        self.connection.remove_listener('connection_encryption_change', self.on_connection_encryption_change)
+        self.connection.remove_listener('connection_encryption_key_refresh', self.on_connection_encryption_key_refresh)
+        self.manager.on_session_end(self)
+
+    def on_connection_encryption_change(self):
+        if self.connection.is_encrypted:
+            if self.is_responder:
+                # The responder distributes its keys first, the initiator later
+                self.distribute_keys()
+
+    def on_connection_encryption_key_refresh(self):
+        # Do as if the connection had just been encrypted
+        self.on_connection_encryption_change()
+
+    def on_pairing(self):
+        logger.debug('pairing complete')
+
+        if self.completed:
+            return
+        else:
+            self.completed = True
+
+        if self.pairing_result is not None and not self.pairing_result.done():
+            self.pairing_result.set_result(None)
+
+        # Use the peer address from the pairing protocol or the connection
+        if self.peer_bd_addr:
+            peer_address = self.peer_bd_addr
+        else:
+            peer_address = self.connection.peer_address
+
+        # Create an object to hold the keys
+        keys = PairingKeys()
+        keys.address_type = peer_address.address_type
+        authenticated = self.pairing_method != self.JUST_WORKS
+        if self.sc:
+            keys.ltk = PairingKeys.Key(
+                value         = self.ltk,
+                authenticated = authenticated
+            )
+        else:
+            our_ltk_key = PairingKeys.Key(
+                value         = self.ltk,
+                authenticated = authenticated,
+                ediv          = self.ltk_ediv,
+                rand          = self.ltk_rand
+            )
+            peer_ltk_key = PairingKeys.Key(
+                value         = self.peer_ltk,
+                authenticated = authenticated,
+                ediv          = self.peer_ediv,
+                rand          = self.peer_rand
+            )
+            if self.is_initiator:
+                keys.ltk_central    = peer_ltk_key
+                keys.ltk_peripheral = our_ltk_key
+            else:
+                keys.ltk_central    = our_ltk_key
+                keys.ltk_peripheral = peer_ltk_key
+        if self.peer_identity_resolving_key is not None:
+            keys.irk = PairingKeys.Key(
+                value         = self.peer_identity_resolving_key,
+                authenticated = authenticated
+            )
+        if self.peer_signature_key is not None:
+            keys.csrk = PairingKeys.Key(
+                value         = self.peer_signature_key,
+                authenticated = authenticated
+            )
+        if self.link_key is not None:
+            keys.link_key = PairingKeys.Key(
+                value         = self.link_key,
+                authenticated = authenticated
+            )
+
+        self.manager.on_pairing(self, peer_address, keys)
+
+    def on_pairing_failure(self, reason):
+        logger.warn(f'pairing failure ({error_name(reason)})')
+
+        if self.completed:
+            return
+        else:
+            self.completed = True
+
+        error = ProtocolError(reason, 'smp', error_name(reason))
+        if self.pairing_result is not None and not self.pairing_result.done():
+            self.pairing_result.set_exception(error)
+        self.manager.on_pairing_failure(self, reason)
+
+    def on_smp_command(self, command):
+        # Find the handler method
+        handler_name = f'on_{command.name.lower()}'
+        handler = getattr(self, handler_name, None)
+        if handler is not None:
+            try:
+                handler(command)
+            except Exception as error:
+                logger.warning(f'{color("!!! Exception in handler:", "red")} {error}')
+                response = SMP_Pairing_Failed_Command(reason = SMP_UNSPECIFIED_REASON_ERROR)
+                self.send_command(response)
+        else:
+            logger.error(color('SMP command not handled???', 'red'))
+
+    def on_smp_pairing_request_command(self, command):
+        asyncio.create_task(self.on_smp_pairing_request_command_async(command))
+
+    async def on_smp_pairing_request_command_async(self, command):
+        # Check if the request should proceed
+        accepted = await self.pairing_config.delegate.accept()
+        if not accepted:
+            logger.debug('pairing rejected by delegate')
+            self.send_pairing_failed(SMP_PAIRING_NOT_SUPPORTED_ERROR)
+            return
+
+        # Save the request
+        self.preq = bytes(command)
+
+        # Bonding and SC require both sides to request/support it
+        self.bonding = self.bonding and (command.auth_req & SMP_BONDING_AUTHREQ != 0)
+        self.sc      = self.sc and (command.auth_req & SMP_SC_AUTHREQ != 0)
+        self.ct2     = self.ct2 and (command.auth_req & SMP_CT2_AUTHREQ != 0)
+
+        # Check for OOB
+        if command.oob_data_flag != 0:
+            self.terminate(SMP_OOB_NOT_AVAILABLE_ERROR)
+            return
+
+        # Decide which pairing method to use
+        self.decide_pairing_method(
+            command.auth_req,
+            command.io_capability,
+            self.io_capability
+        )
+        logger.debug(f'pairing method: {self.PAIRING_METHOD_NAMES[self.pairing_method]}')
+
+        # Key distribution
+        self.initiator_key_distribution, self.responder_key_distribution = await self.pairing_config.delegate.key_distribution_response(
+            command.initiator_key_distribution, command.responder_key_distribution)
+        self.compute_peer_expected_distributions(self.initiator_key_distribution)
+
+        # The pairing is now starting
+        self.manager.on_session_start(self)
+
+        # Display a passkey if we need to
+        if not self.sc:
+            if self.pairing_method == self.PASSKEY and self.passkey_display:
+                self.display_passkey()
+
+        # Respond
+        self.send_pairing_response_command()
+
+    def on_smp_pairing_response_command(self, command):
+        if self.is_responder:
+            logger.warn(color('received pairing response as a responder', 'red'))
+            return
+
+        # Save the response
+        self.pres = bytes(command)
+        self.peer_io_capability = command.io_capability
+
+        # Bonding and SC require both sides to request/support it
+        self.bonding = self.bonding and (command.auth_req & SMP_BONDING_AUTHREQ != 0)
+        self.sc      = self.sc and (command.auth_req & SMP_SC_AUTHREQ != 0)
+
+        # Check for OOB
+        if self.sc and command.oob_data_flag:
+            self.send_pairing_failed(SMP_OOB_NOT_AVAILABLE_ERROR)
+            return
+
+        # Decide which pairing method to use
+        self.decide_pairing_method(
+            command.auth_req,
+            self.io_capability,
+            command.io_capability
+        )
+        logger.debug(f'pairing method: {self.PAIRING_METHOD_NAMES[self.pairing_method]}')
+
+        # Key distribution
+        if (command.initiator_key_distribution & ~self.initiator_key_distribution != 0) or \
+           (command.responder_key_distribution & ~self.responder_key_distribution != 0):
+            # The response isn't a subset of the request
+            self.send_pairing_failed(SMP_INVALID_PARAMETERS_ERROR)
+            return
+        self.initiator_key_distribution = command.initiator_key_distribution
+        self.responder_key_distribution = command.responder_key_distribution
+        self.compute_peer_expected_distributions(self.responder_key_distribution)
+
+        # Start phase 2
+        if self.sc:
+            if self.pairing_method == self.PASSKEY and self.passkey_display:
+                self.display_passkey()
+
+            self.send_public_key_command()
+        else:
+            if self.pairing_method == self.PASSKEY:
+                self.display_or_input_passkey(self.send_pairing_confirm_command)
+            else:
+                self.send_pairing_confirm_command()
+
+    def on_smp_pairing_confirm_command_legacy(self, command):
+        if self.is_initiator:
+            self.send_pairing_random_command()
+        else:
+            # If the method is PASSKEY, now is the time to input the code
+            if self.pairing_method == self.PASSKEY and not self.passkey_display:
+                self.input_passkey(self.send_pairing_confirm_command)
+            else:
+                self.send_pairing_confirm_command()
+
+    def on_smp_pairing_confirm_command_secure_connections(self, command):
+        if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+            if self.is_initiator:
+                self.r = crypto.r()
+                self.send_pairing_random_command()
+        elif self.pairing_method == self.PASSKEY:
+            if self.is_initiator:
+                self.send_pairing_random_command()
+            else:
+                self.send_pairing_confirm_command()
+
+    def on_smp_pairing_confirm_command(self, command):
+        self.confirm_value = command.confirm_value
+        if self.sc:
+            self.on_smp_pairing_confirm_command_secure_connections(command)
+        else:
+            self.on_smp_pairing_confirm_command_legacy(command)
+
+    def on_smp_pairing_random_command_legacy(self, command):
+        # Check that the confirmation values match
+        confirm_verifier = crypto.c1(
+            self.tk,
+            command.random_value,
+            self.preq,
+            self.pres,
+            self.iat,
+            self.rat,
+            self.ia,
+            self.ra
+        )
+        if not self.check_expected_value(
+            self.confirm_value,
+            confirm_verifier,
+            SMP_CONFIRM_VALUE_FAILED_ERROR
+        ):
+            return
+
+        # Compute STK
+        if self.is_initiator:
+            mrand = self.r
+            srand = command.random_value
+        else:
+            srand = self.r
+            mrand = command.random_value
+        stk = crypto.s1(self.tk, srand, mrand)
+        logger.debug(f'STK = {stk.hex()}')
+
+        # Generate LTK
+        self.ltk = crypto.r()
+
+        if self.is_initiator:
+            self.start_encryption(stk)
+        else:
+            self.send_pairing_random_command()
+
+    def on_smp_pairing_random_command_secure_connections(self, command):
+        if self.is_initiator:
+            if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+                # Check that the random value matches what was committed to earlier
+                confirm_verifier = crypto.f4(
+                    self.pkb,
+                    self.pka,
+                    command.random_value,
+                    bytes([0])
+                )
+                if not self.check_expected_value(
+                    self.confirm_value,
+                    confirm_verifier,
+                    SMP_CONFIRM_VALUE_FAILED_ERROR
+                ):
+                    return
+            elif self.pairing_method == self.PASSKEY:
+                # Check that the random value matches what was committed to earlier
+                confirm_verifier = crypto.f4(
+                    self.pkb,
+                    self.pka,
+                    command.random_value,
+                    bytes([0x80 + ((self.passkey >> self.passkey_step) & 1)])
+                )
+                if not self.check_expected_value(
+                    self.confirm_value,
+                    confirm_verifier,
+                    SMP_CONFIRM_VALUE_FAILED_ERROR
+                ):
+                    return
+
+                # Move on to the next iteration
+                self.passkey_step += 1
+                logger.debug(f'passkey finished step {self.passkey_step} of 20')
+                if self.passkey_step < 20:
+                    self.send_pairing_confirm_command()
+                    return
+            else:
+                return
+        else:
+            if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+                self.send_pairing_random_command()
+            elif self.pairing_method == self.PASSKEY:
+                # Check that the random value matches what was committed to earlier
+                confirm_verifier = crypto.f4(
+                    self.pka,
+                    self.pkb,
+                    command.random_value,
+                    bytes([0x80 + ((self.passkey >> self.passkey_step) & 1)])
+                )
+                if not self.check_expected_value(
+                    self.confirm_value,
+                    confirm_verifier,
+                    SMP_CONFIRM_VALUE_FAILED_ERROR
+                ):
+                    return
+
+                self.send_pairing_random_command()
+
+                # Move on to the next iteration
+                self.passkey_step += 1
+                logger.debug(f'passkey finished step {self.passkey_step} of 20')
+                if self.passkey_step < 20:
+                    self.r = crypto.r()
+                    return
+            else:
+                return
+
+        # Compute the MacKey and LTK
+        a = self.ia + bytes([self.iat])
+        b = self.ra + bytes([self.rat])
+        (mac_key, self.ltk) = crypto.f5(self.dh_key, self.na, self.nb, a, b)
+
+        # Compute the DH Key checks
+        if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+            ra = bytes(16)
+            rb = ra
+        elif self.pairing_method == self.PASSKEY:
+            ra = self.passkey.to_bytes(16, byteorder='little')
+            rb = ra
+        else:
+            # OOB not implemented yet
+            return
+
+        io_cap_a = self.preq[1:4]
+        io_cap_b = self.pres[1:4]
+        self.ea = crypto.f6(mac_key, self.na, self.nb, rb, io_cap_a, a, b)
+        self.eb = crypto.f6(mac_key, self.nb, self.na, ra, io_cap_b, b, a)
+
+        # Next steps to be performed after possible user confirmation
+        def next_steps():
+            # The initiator sends the DH Key check to the responder
+            if self.is_initiator:
+                self.send_pairing_dhkey_check_command()
+            else:
+                if self.wait_before_continuing:
+                    self.wait_before_continuing.set_result(None)
+
+        # Prompt the user for confirmation if needed
+        if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+            # Compute the 6-digit code
+            code = crypto.g2(self.pka, self.pkb, self.na, self.nb) % 1000000
+
+            if self.pairing_method == self.NUMERIC_COMPARISON:
+                # Ask for user confirmation
+                self.wait_before_continuing = asyncio.get_running_loop().create_future()
+                self.prompt_user_for_numeric_comparison(code, next_steps)
+            else:
+                next_steps()
+        else:
+            next_steps()
+
+    def on_smp_pairing_random_command(self, command):
+        self.peer_random_value = command.random_value
+        if self.sc:
+            self.on_smp_pairing_random_command_secure_connections(command)
+        else:
+            self.on_smp_pairing_random_command_legacy(command)
+
+    def on_smp_pairing_public_key_command(self, command):
+        # Store the public key so that we can compute the confirmation value later
+        self.peer_public_key_x = command.public_key_x
+        self.peer_public_key_y = command.public_key_y
+
+        # Compute the DH key
+        self.dh_key = bytes(reversed(self.manager.ecc_key.dh(
+            bytes(reversed(command.public_key_x)),
+            bytes(reversed(command.public_key_y))
+        )))
+        logger.debug(f'DH key: {self.dh_key.hex()}')
+
+        if self.is_initiator:
+            if self.pairing_method == self.PASSKEY:
+                if self.passkey_display:
+                    self.send_pairing_confirm_command()
+                else:
+                    self.input_passkey(self.send_pairing_confirm_command)
+        else:
+            # Send our public key back to the initiator
+            if self.pairing_method == self.PASSKEY:
+                self.display_or_input_passkey(self.send_public_key_command)
+            else:
+                self.send_public_key_command()
+
+            if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON:
+                # We can now send the confirmation value
+                self.send_pairing_confirm_command()
+
+    def on_smp_pairing_dhkey_check_command(self, command):
+        # Check that what we received matches what we computed earlier
+        expected = self.eb if self.is_initiator else self.ea
+        if not self.check_expected_value(
+            expected,
+            command.dhkey_check,
+            SMP_DHKEY_CHECK_FAILED_ERROR
+        ):
+            return
+
+        if self.is_responder:
+            if self.wait_before_continuing is not None:
+                async def next_steps():
+                    await self.wait_before_continuing
+                    self.wait_before_continuing = None
+                    self.send_pairing_dhkey_check_command()
+
+                asyncio.create_task(next_steps())
+            else:
+                self.send_pairing_dhkey_check_command()
+        else:
+            self.start_encryption(self.ltk)
+
+    def on_smp_pairing_failed_command(self, command):
+        self.on_pairing_failure(command.reason)
+
+    def on_smp_encryption_information_command(self, command):
+        self.peer_ltk = command.long_term_key
+        self.check_key_distribution(SMP_Encryption_Information_Command)
+
+    def on_smp_master_identification_command(self, command):
+        self.peer_ediv = command.ediv
+        self.peer_rand = command.rand
+        self.check_key_distribution(SMP_Master_Identification_Command)
+
+    def on_smp_identity_information_command(self, command):
+        self.peer_identity_resolving_key = command.identity_resolving_key
+        self.check_key_distribution(SMP_Identity_Information_Command)
+
+    def on_smp_identity_address_information_command(self, command):
+        self.peer_bd_addr = command.bd_addr
+        self.check_key_distribution(SMP_Identity_Address_Information_Command)
+
+    def on_smp_signing_information_command(self, command):
+        self.peer_signature_key = command.signature_key
+        self.check_key_distribution(SMP_Signing_Information_Command)
+
+
+# -----------------------------------------------------------------------------
+class Manager(EventEmitter):
+    '''
+    Implements the Initiator and Responder roles of the Security Manager Protocol
+    '''
+
+    def __init__(self, device, address):
+        super().__init__()
+        self.device                 = device
+        self.address                = address
+        self.sessions               = {}
+        self._ecc_key               = None
+        self.pairing_config_factory = lambda connection: PairingConfig()
+
+    def send_command(self, connection, command):
+        logger.debug(f'>>> Sending SMP Command on connection [0x{connection.handle:04X}] {connection.peer_address}: {command}')
+        connection.send_l2cap_pdu(SMP_CID, command.to_bytes())
+
+    def on_smp_pdu(self, connection, pdu):
+        # Look for a session with this connection, and create one if none exists
+        if not (session := self.sessions.get(connection.handle)):
+            pairing_config = self.pairing_config_factory(connection)
+            if pairing_config is None:
+                # Pairing disabled
+                self.send_command(
+                    connection,
+                    SMP_Pairing_Failed_Command(
+                        reason = SMP_PAIRING_NOT_SUPPORTED_ERROR
+                    )
+                )
+                return
+            session = Session(self, connection, pairing_config)
+            self.sessions[connection.handle] = session
+
+        # Parse the L2CAP payload into an SMP Command object
+        command = SMP_Command.from_bytes(pdu)
+        logger.debug(f'<<< Received SMP Command on connection [0x{connection.handle:04X}] {connection.peer_address}: {command}')
+
+        # Delegate the handling of the command to the session
+        session.on_smp_command(command)
+
+    @property
+    def ecc_key(self):
+        if self._ecc_key is None:
+            self._ecc_key = crypto.EccKey.generate()
+        return self._ecc_key
+
+    async def pair(self, connection):
+        # TODO: check if there's already a session for this connection
+        pairing_config = self.pairing_config_factory(connection)
+        if pairing_config is None:
+            raise ValueError('pairing config must not be None when initiating')
+        session = Session(self, connection, pairing_config)
+        self.sessions[connection.handle] = session
+        return await session.pair()
+
+    def request_pairing(self, connection):
+        pairing_config = self.pairing_config_factory(connection)
+        if pairing_config:
+            auth_req = smp_auth_req(
+                pairing_config.bonding,
+                pairing_config.mitm,
+                pairing_config.sc,
+                False,
+                False
+            )
+        else:
+            auth_req = 0
+        self.send_command(connection, SMP_Security_Request_Command(auth_req=auth_req))
+
+    def on_session_start(self, session):
+        self.device.on_pairing_start(session.connection.handle)
+
+    def on_pairing(self, session, identity_address, keys):
+        # Store the keys in the key store
+        if self.device.keystore and identity_address is not None:
+            async def store_keys():
+                try:
+                    await self.device.keystore.update(str(identity_address), keys)
+                except Exception as error:
+                    logger.warn(f'!!! error while storing keys: {error}')
+            asyncio.create_task(store_keys())
+
+        # Notify the device
+        self.device.on_pairing(session.connection.handle, keys)
+
+    def on_pairing_failure(self, session, reason):
+        self.device.on_pairing_failure(session.connection.handle, reason)
+
+    def on_session_end(self, session):
+        logger.debug(f'session end for connection 0x{session.connection.handle:04X}')
+        if session.connection.handle in self.sessions:
+            del self.sessions[session.connection.handle]
+
+    def get_long_term_key(self, connection, rand, ediv):
+        if session := self.sessions.get(connection.handle):
+            return session.get_long_term_key(rand, ediv)
diff --git a/bumble/transport/__init__.py b/bumble/transport/__init__.py
new file mode 100644
index 0000000..c3bd5f8
--- /dev/null
+++ b/bumble/transport/__init__.py
@@ -0,0 +1,95 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+
+from .common import Transport, AsyncPipeSink
+from ..link import RemoteLink
+from ..controller import Controller
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_transport(name):
+    '''
+    Open a transport by name.
+    The name must be <type>:<parameters>
+    Where <parameters> depend on the type (and may be empty for some types).
+    The supported types are: serial,udp,tcp,pty,usb
+    '''
+    scheme, *spec = name.split(':', 1)
+    if scheme == 'serial' and spec:
+        from .serial import open_serial_transport
+        return await open_serial_transport(spec[0])
+    elif scheme == 'udp' and spec:
+        from .udp import open_udp_transport
+        return await open_udp_transport(spec[0])
+    elif scheme == 'tcp-client' and spec:
+        from .tcp_client import open_tcp_client_transport
+        return await open_tcp_client_transport(spec[0])
+    elif scheme == 'tcp-server' and spec:
+        from .tcp_server import open_tcp_server_transport
+        return await open_tcp_server_transport(spec[0])
+    elif scheme == 'ws-client' and spec:
+        from .ws_client import open_ws_client_transport
+        return await open_ws_client_transport(spec[0])
+    elif scheme == 'ws-server' and spec:
+        from .ws_server import open_ws_server_transport
+        return await open_ws_server_transport(spec[0])
+    elif scheme == 'pty':
+        from .pty import open_pty_transport
+        return await open_pty_transport(spec[0] if spec else None)
+    elif scheme == 'file':
+        from .file import open_file_transport
+        return await open_file_transport(spec[0] if spec else None)
+    elif scheme == 'vhci':
+        from .vhci import open_vhci_transport
+        return await open_vhci_transport(spec[0] if spec else None)
+    elif scheme == 'hci-socket':
+        from .hci_socket import open_hci_socket_transport
+        return await open_hci_socket_transport(spec[0] if spec else None)
+    elif scheme == 'usb':
+        from .usb import open_usb_transport
+        return await open_usb_transport(spec[0] if spec else None)
+    elif scheme == 'pyusb':
+        from .pyusb import open_pyusb_transport
+        return await open_pyusb_transport(spec[0] if spec else None)
+    elif scheme == 'android-emulator':
+        from .android_emulator import open_android_emulator_transport
+        return await open_android_emulator_transport(spec[0] if spec else None)
+    else:
+        raise ValueError('unknown transport scheme')
+
+
+# -----------------------------------------------------------------------------
+async def open_transport_or_link(name):
+    if name.startswith('link-relay:'):
+        link = RemoteLink(name[11:])
+        await link.wait_until_connected()
+        controller = Controller('remote', link = link)
+
+        class LinkTransport(Transport):
+            async def close(self):
+                link.close()
+
+        return LinkTransport(controller, AsyncPipeSink(controller))
+    else:
+        return await open_transport(name)
diff --git a/bumble/transport/android_emulator.py b/bumble/transport/android_emulator.py
new file mode 100644
index 0000000..d27aef6
--- /dev/null
+++ b/bumble/transport/android_emulator.py
@@ -0,0 +1,107 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import grpc
+
+from .common import PumpedTransport, PumpedPacketSource, PumpedPacketSink
+from .emulated_bluetooth_pb2_grpc import EmulatedBluetoothServiceStub
+from .emulated_bluetooth_packets_pb2 import HCIPacket
+from .emulated_bluetooth_vhci_pb2_grpc import VhciForwardingServiceStub
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_android_emulator_transport(spec):
+    '''
+    Open a transport connection to an Android emulator via its gRPC interface.
+    The parameter string has this syntax:
+    [<remote-host>:<remote-port>][,mode=<host|controller>]
+    The <remote-host>:<remote-port> part is optional, it defaults to localhost:8554
+    The mode=<mode> part is optional, it defaults to mode=host
+    When the mode is set to 'controller', the connection is for a controller (i.e the
+    Android Bluetooth stack will use the connected endpoint as its controller). When
+    the mode is set to 'host', the connection is to the 'Root Canal' virtual controller
+    that runs as part of the emulator, and used by the Android Bluetooth stack.
+
+    Examples:
+    (empty string) --> connect as a host to the emulator on localhost:8554
+    localhost:8555 --> connect as a host to the emulator on localhost:8555
+    mode=controller --> connect as a controller to the emulator on localhost:8554
+    '''
+
+    # Wrapper for I/O operations
+    class HciDevice:
+        def __init__(self, hci_device):
+            self.hci_device = hci_device
+
+        async def read(self):
+            packet = await self.hci_device.read()
+            return bytes([packet.type]) + packet.packet
+
+        async def write(self, packet):
+            await self.hci_device.write(
+                HCIPacket(
+                    type   = packet[0],
+                    packet = packet[1:]
+                )
+            )
+
+    # Parse the parameters
+    mode        = 'host'
+    server_host = 'localhost'
+    server_port = 8554
+    if spec is not None:
+        params = spec.split(',')
+        for param in params:
+            if param.startswith('mode='):
+                mode = param.split('=')[1]
+            elif ':' in param:
+                server_host, server_port = param.split(':')
+            else:
+                raise ValueError('invalid parameter')
+
+    # Connect to the gRPC server
+    server_address = f'{server_host}:{server_port}'
+    logger.debug(f'connecting to gRPC server at {server_address}')
+    channel = grpc.aio.insecure_channel(server_address)
+
+    if mode == 'host':
+        # Connect as a host
+        service = EmulatedBluetoothServiceStub(channel)
+        hci_device = HciDevice(service.registerHCIDevice())
+    elif mode == 'controller':
+        # Connect as a controller
+        service = VhciForwardingServiceStub(channel)
+        hci_device = HciDevice(service.attachVhci())
+    else:
+        raise ValueError('invalid mode')
+
+    # Create the transport object
+    transport = PumpedTransport(
+        PumpedPacketSource(hci_device.read),
+        PumpedPacketSink(hci_device.write),
+        channel.close
+    )
+    transport.start()
+
+    return transport
diff --git a/bumble/transport/common.py b/bumble/transport/common.py
new file mode 100644
index 0000000..d5c1ae9
--- /dev/null
+++ b/bumble/transport/common.py
@@ -0,0 +1,326 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import struct
+import asyncio
+import logging
+from colors import color
+
+from .. import hci
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+# -----------------------------------------------------------------------------
+# Information needed to parse HCI packets with a generic parser:
+# For each packet type, the info represents:
+# (length-size, length-offset, unpack-type)
+HCI_PACKET_INFO = {
+    hci.HCI_COMMAND_PACKET:          (1, 2, 'B'),
+    hci.HCI_ACL_DATA_PACKET:         (2, 2, 'H'),
+    hci.HCI_SYNCHRONOUS_DATA_PACKET: (1, 2, 'B'),
+    hci.HCI_EVENT_PACKET:            (1, 1, 'B')
+}
+
+
+# -----------------------------------------------------------------------------
+class PacketPump:
+    '''
+    Pump HCI packets from a reader to a sink
+    '''
+
+    def __init__(self, reader, sink):
+        self.reader = reader
+        self.sink   = sink
+
+    async def run(self):
+        while True:
+            try:
+                # Get a packet from the source
+                packet = hci.HCI_Packet.from_bytes(await self.reader.next_packet())
+
+                # Deliver the packet to the sink
+                self.sink.on_packet(packet)
+            except Exception as error:
+                logger.warning(f'!!! {error}')
+
+
+# -----------------------------------------------------------------------------
+class PacketParser:
+    '''
+    In-line parser that accepts data and emits 'on_packet' when a full packet has been parsed
+    '''
+    NEED_TYPE   = 0
+    NEED_LENGTH = 1
+    NEED_BODY   = 2
+
+    def __init__(self, sink = None):
+        self.sink = sink
+        self.extended_packet_info = {}
+        self.reset()
+
+    def reset(self):
+        self.state        = PacketParser.NEED_TYPE
+        self.bytes_needed = 1
+        self.packet       = bytearray()
+        self.packet_info  = None
+
+    def feed_data(self, data):
+        data_offset = 0
+        data_left = len(data)
+        while data_left and self.bytes_needed:
+            consumed = min(self.bytes_needed, data_left)
+            self.packet.extend(data[data_offset:data_offset + consumed])
+            data_offset       += consumed
+            data_left         -= consumed
+            self.bytes_needed -= consumed
+
+            if self.bytes_needed == 0:
+                if self.state == PacketParser.NEED_TYPE:
+                    packet_type = self.packet[0]
+                    self.packet_info  = HCI_PACKET_INFO.get(packet_type) or self.extended_packet_info.get(packet_type)
+                    if self.packet_info is None:
+                        raise ValueError(f'invalid packet type {packet_type}')
+                    self.state = PacketParser.NEED_LENGTH
+                    self.bytes_needed = self.packet_info[0] + self.packet_info[1]
+                elif self.state == PacketParser.NEED_LENGTH:
+                    body_length = struct.unpack_from(self.packet_info[2], self.packet, 1 + self.packet_info[1])[0]
+                    self.bytes_needed = body_length
+                    self.state = PacketParser.NEED_BODY
+
+                # Emit a packet if one is complete
+                if self.state == PacketParser.NEED_BODY and not self.bytes_needed:
+                    if self.sink:
+                        try:
+                            self.sink.on_packet(bytes(self.packet))
+                        except Exception as error:
+                            logger.warning(color(f'!!! Exception in on_packet: {error}', 'red'))
+                    self.reset()
+
+    def set_packet_sink(self, sink):
+        self.sink = sink
+
+
+# -----------------------------------------------------------------------------
+class PacketReader:
+    '''
+    Reader that reads HCI packets from a sync source
+    '''
+
+    def __init__(self, source):
+        self.source = source
+
+    def next_packet(self):
+        # Get the packet type
+        packet_type = self.source.read(1)
+        if len(packet_type) != 1:
+            return None
+
+        # Get the packet info based on its type
+        packet_info = HCI_PACKET_INFO.get(packet_type[0])
+        if packet_info is None:
+            raise ValueError(f'invalid packet type {packet_type} found')
+
+        # Read the header (that includes the length)
+        header_size = packet_info[0] + packet_info[1]
+        header = self.source.read(header_size)
+        if len(header) != header_size:
+            raise ValueError('packet too short')
+
+        # Read the body
+        body_length = struct.unpack_from(packet_info[2], header, packet_info[1])[0]
+        body = self.source.read(body_length)
+        if len(body) != body_length:
+            raise ValueError('packet too short')
+
+        return packet_type + header + body
+
+
+# -----------------------------------------------------------------------------
+class AsyncPacketReader:
+    '''
+    Reader that reads HCI packets from an async source
+    '''
+
+    def __init__(self, source):
+        self.source = source
+
+    async def next_packet(self):
+        # Get the packet type
+        packet_type = await self.source.readexactly(1)
+
+        # Get the packet info based on its type
+        packet_info = HCI_PACKET_INFO.get(packet_type[0])
+        if packet_info is None:
+            raise ValueError(f'invalid packet type {packet_type} found')
+
+        # Read the header (that includes the length)
+        header_size = packet_info[0] + packet_info[1]
+        header = await self.source.readexactly(header_size)
+
+        # Read the body
+        body_length = struct.unpack_from(packet_info[2], header, packet_info[1])[0]
+        body = await self.source.readexactly(body_length)
+
+        return packet_type + header + body
+
+
+# -----------------------------------------------------------------------------
+class AsyncPipeSink:
+    '''
+    Sink that forwards packets asynchronously to another sink
+    '''
+    def __init__(self, sink):
+        self.sink = sink
+        self.loop = asyncio.get_running_loop()
+
+    def on_packet(self, packet):
+        self.loop.call_soon(self.sink.on_packet, packet)
+
+
+# -----------------------------------------------------------------------------
+class ParserSource:
+    """
+    Base class designed to be subclassed by transport-specific source classes
+    """
+
+    def __init__(self):
+        self.parser     = PacketParser()
+        self.terminated = asyncio.get_running_loop().create_future()
+
+    def set_packet_sink(self, sink):
+        self.parser.set_packet_sink(sink)
+
+    async def wait_for_termination(self):
+        return await self.terminated
+
+    def close(self):
+        pass
+
+
+# -----------------------------------------------------------------------------
+class StreamPacketSource(asyncio.Protocol, ParserSource):
+    def data_received(self, data):
+        self.parser.feed_data(data)
+
+
+# -----------------------------------------------------------------------------
+class StreamPacketSink:
+    def __init__(self, transport):
+        self.transport = transport
+
+    def on_packet(self, packet):
+        self.transport.write(packet)
+
+    def close(self):
+        self.transport.close()
+
+
+# -----------------------------------------------------------------------------
+class Transport:
+    def __init__(self, source, sink):
+        self.source = source
+        self.sink   = sink
+
+    async def __aenter__(self):
+        return self
+
+    async def __aexit__(self, *args):
+        await self.close()
+
+    def __iter__(self):
+        return iter((self.source, self.sink))
+
+    async def close(self):
+        self.source.close()
+        self.sink.close()
+
+
+# -----------------------------------------------------------------------------
+class PumpedPacketSource(ParserSource):
+    def __init__(self, receive):
+        super().__init__()
+        self.receive_function = receive
+        self.pump_task        = None
+
+    def start(self):
+        async def pump_packets():
+            while True:
+                try:
+                    packet = await self.receive_function()
+                    self.parser.feed_data(packet)
+                except asyncio.exceptions.CancelledError:
+                    logger.debug('source pump task done')
+                    break
+                except Exception as error:
+                    logger.warn(f'exception while waiting for packet: {error}')
+                    self.terminated.set_result(error)
+                    break
+
+        self.pump_task = asyncio.get_running_loop().create_task(pump_packets())
+
+    def close(self):
+        if self.pump_task:
+            self.pump_task.cancel()
+
+
+# -----------------------------------------------------------------------------
+class PumpedPacketSink:
+    def __init__(self, send):
+        self.send_function = send
+        self.packet_queue  = asyncio.Queue()
+        self.pump_task     = None
+
+    def on_packet(self, packet):
+        self.packet_queue.put_nowait(packet)
+
+    def start(self):
+        async def pump_packets():
+            while True:
+                try:
+                    packet = await self.packet_queue.get()
+                    await self.send_function(packet)
+                except asyncio.exceptions.CancelledError:
+                    logger.debug('sink pump task done')
+                    break
+                except Exception as error:
+                    logger.warn(f'exception while sending packet: {error}')
+                    break
+
+        self.pump_task = asyncio.get_running_loop().create_task(pump_packets())
+
+    def close(self):
+        if self.pump_task:
+            self.pump_task.cancel()
+
+
+# -----------------------------------------------------------------------------
+class PumpedTransport(Transport):
+    def __init__(self, source, sink, close_function):
+        super().__init__(source, sink)
+        self.close_function = close_function
+
+    def start(self):
+        self.source.start()
+        self.sink.start()
+
+    async def close(self):
+        await super().close()
+        await self.close_function()
diff --git a/bumble/transport/emulated_bluetooth_packets_pb2.py b/bumble/transport/emulated_bluetooth_packets_pb2.py
new file mode 100644
index 0000000..9d3591d
--- /dev/null
+++ b/bumble/transport/emulated_bluetooth_packets_pb2.py
@@ -0,0 +1,52 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: emulated_bluetooth_packets.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n emulated_bluetooth_packets.proto\x12\x1b\x61ndroid.emulation.bluetooth\"\xfb\x01\n\tHCIPacket\x12?\n\x04type\x18\x01 \x01(\x0e\x32\x31.android.emulation.bluetooth.HCIPacket.PacketType\x12\x0e\n\x06packet\x18\x02 \x01(\x0c\"\x9c\x01\n\nPacketType\x12\x1b\n\x17PACKET_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17PACKET_TYPE_HCI_COMMAND\x10\x01\x12\x13\n\x0fPACKET_TYPE_ACL\x10\x02\x12\x13\n\x0fPACKET_TYPE_SCO\x10\x03\x12\x15\n\x11PACKET_TYPE_EVENT\x10\x04\x12\x13\n\x0fPACKET_TYPE_ISO\x10\x05\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3')
+
+
+
+_HCIPACKET = DESCRIPTOR.message_types_by_name['HCIPacket']
+_HCIPACKET_PACKETTYPE = _HCIPACKET.enum_types_by_name['PacketType']
+HCIPacket = _reflection.GeneratedProtocolMessageType('HCIPacket', (_message.Message,), {
+  'DESCRIPTOR' : _HCIPACKET,
+  '__module__' : 'emulated_bluetooth_packets_pb2'
+  # @@protoc_insertion_point(class_scope:android.emulation.bluetooth.HCIPacket)
+  })
+_sym_db.RegisterMessage(HCIPacket)
+
+if _descriptor._USE_C_DESCRIPTORS == False:
+
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\037com.android.emulation.bluetoothP\001\370\001\001\242\002\003AEB\252\002\033Android.Emulation.Bluetooth'
+  _HCIPACKET._serialized_start=66
+  _HCIPACKET._serialized_end=317
+  _HCIPACKET_PACKETTYPE._serialized_start=161
+  _HCIPACKET_PACKETTYPE._serialized_end=317
+# @@protoc_insertion_point(module_scope)
diff --git a/bumble/transport/emulated_bluetooth_pb2.py b/bumble/transport/emulated_bluetooth_pb2.py
new file mode 100644
index 0000000..4da12d5
--- /dev/null
+++ b/bumble/transport/emulated_bluetooth_pb2.py
@@ -0,0 +1,53 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: emulated_bluetooth.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x65mulated_bluetooth.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto\"\x19\n\x07RawData\x12\x0e\n\x06packet\x18\x01 \x01(\x0c\x32\xcb\x02\n\x18\x45mulatedBluetoothService\x12\x64\n\x12registerClassicPhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12`\n\x0eregisterBlePhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12g\n\x11registerHCIDevice\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x42\"\n\x1e\x63om.android.emulator.bluetoothP\x01\x62\x06proto3')
+
+
+
+_RAWDATA = DESCRIPTOR.message_types_by_name['RawData']
+RawData = _reflection.GeneratedProtocolMessageType('RawData', (_message.Message,), {
+  'DESCRIPTOR' : _RAWDATA,
+  '__module__' : 'emulated_bluetooth_pb2'
+  # @@protoc_insertion_point(class_scope:android.emulation.bluetooth.RawData)
+  })
+_sym_db.RegisterMessage(RawData)
+
+_EMULATEDBLUETOOTHSERVICE = DESCRIPTOR.services_by_name['EmulatedBluetoothService']
+if _descriptor._USE_C_DESCRIPTORS == False:
+
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\036com.android.emulator.bluetoothP\001'
+  _RAWDATA._serialized_start=91
+  _RAWDATA._serialized_end=116
+  _EMULATEDBLUETOOTHSERVICE._serialized_start=119
+  _EMULATEDBLUETOOTHSERVICE._serialized_end=450
+# @@protoc_insertion_point(module_scope)
diff --git a/bumble/transport/emulated_bluetooth_pb2_grpc.py b/bumble/transport/emulated_bluetooth_pb2_grpc.py
new file mode 100644
index 0000000..cc0ce37
--- /dev/null
+++ b/bumble/transport/emulated_bluetooth_pb2_grpc.py
@@ -0,0 +1,207 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+"""Client and server classes corresponding to protobuf-defined services."""
+import grpc
+
+from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2
+from . import emulated_bluetooth_pb2 as emulated__bluetooth__pb2
+
+
+class EmulatedBluetoothServiceStub(object):
+    """An Emulated Bluetooth Service exposes the emulated bluetooth chip from the
+    android emulator. It allows you to register emulated bluetooth devices and
+    control the packets that are exchanged between the device and the world.
+
+    This service enables you to establish a "virtual network" of emulated
+    bluetooth devices that can interact with each other.
+
+    Note: This is not yet finalized, it is likely that these definitions will
+    evolve.
+    """
+
+    def __init__(self, channel):
+        """Constructor.
+
+        Args:
+            channel: A grpc.Channel.
+        """
+        self.registerClassicPhy = channel.stream_stream(
+                '/android.emulation.bluetooth.EmulatedBluetoothService/registerClassicPhy',
+                request_serializer=emulated__bluetooth__pb2.RawData.SerializeToString,
+                response_deserializer=emulated__bluetooth__pb2.RawData.FromString,
+                )
+        self.registerBlePhy = channel.stream_stream(
+                '/android.emulation.bluetooth.EmulatedBluetoothService/registerBlePhy',
+                request_serializer=emulated__bluetooth__pb2.RawData.SerializeToString,
+                response_deserializer=emulated__bluetooth__pb2.RawData.FromString,
+                )
+        self.registerHCIDevice = channel.stream_stream(
+                '/android.emulation.bluetooth.EmulatedBluetoothService/registerHCIDevice',
+                request_serializer=emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString,
+                response_deserializer=emulated__bluetooth__packets__pb2.HCIPacket.FromString,
+                )
+
+
+class EmulatedBluetoothServiceServicer(object):
+    """An Emulated Bluetooth Service exposes the emulated bluetooth chip from the
+    android emulator. It allows you to register emulated bluetooth devices and
+    control the packets that are exchanged between the device and the world.
+
+    This service enables you to establish a "virtual network" of emulated
+    bluetooth devices that can interact with each other.
+
+    Note: This is not yet finalized, it is likely that these definitions will
+    evolve.
+    """
+
+    def registerClassicPhy(self, request_iterator, context):
+        """Connect device to link layer. This will establish a direct connection
+        to the emulated bluetooth chip and configure the following:
+
+        - Each connection creates a new device and attaches it to the link layer
+        - Link Layer packets are transmitted directly to the phy
+
+        This should be used for classic connections.
+
+        This is used to directly connect various android emulators together.
+        For example a wear device can connect to an android emulator through
+        this.
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def registerBlePhy(self, request_iterator, context):
+        """Connect device to link layer. This will establish a direct connection
+        to root canal and execute the following:
+
+        - Each connection creates a new device and attaches it to the link layer
+        - Link Layer packets are transmitted directly to the phy
+
+        This should be used for BLE connections.
+
+        This is used to directly connect various android emulators together.
+        For example a wear device can connect to an android emulator through
+        this.
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def registerHCIDevice(self, request_iterator, context):
+        """Connect the device to the emulated bluetooth chip. The device will
+        participate in the network. You can configure the chip to scan, advertise
+        and setup connections with other devices that are connected to the
+        network.
+
+        This is usually used when you have a need for an emulated bluetooth chip
+        and have a bluetooth stack that can interpret and handle the packets
+        correctly.
+
+        For example the apache nimble stack can use this endpoint as the
+        transport layer.
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+
+def add_EmulatedBluetoothServiceServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+            'registerClassicPhy': grpc.stream_stream_rpc_method_handler(
+                    servicer.registerClassicPhy,
+                    request_deserializer=emulated__bluetooth__pb2.RawData.FromString,
+                    response_serializer=emulated__bluetooth__pb2.RawData.SerializeToString,
+            ),
+            'registerBlePhy': grpc.stream_stream_rpc_method_handler(
+                    servicer.registerBlePhy,
+                    request_deserializer=emulated__bluetooth__pb2.RawData.FromString,
+                    response_serializer=emulated__bluetooth__pb2.RawData.SerializeToString,
+            ),
+            'registerHCIDevice': grpc.stream_stream_rpc_method_handler(
+                    servicer.registerHCIDevice,
+                    request_deserializer=emulated__bluetooth__packets__pb2.HCIPacket.FromString,
+                    response_serializer=emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString,
+            ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+            'android.emulation.bluetooth.EmulatedBluetoothService', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+ # This class is part of an EXPERIMENTAL API.
+class EmulatedBluetoothService(object):
+    """An Emulated Bluetooth Service exposes the emulated bluetooth chip from the
+    android emulator. It allows you to register emulated bluetooth devices and
+    control the packets that are exchanged between the device and the world.
+
+    This service enables you to establish a "virtual network" of emulated
+    bluetooth devices that can interact with each other.
+
+    Note: This is not yet finalized, it is likely that these definitions will
+    evolve.
+    """
+
+    @staticmethod
+    def registerClassicPhy(request_iterator,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.stream_stream(request_iterator, target, '/android.emulation.bluetooth.EmulatedBluetoothService/registerClassicPhy',
+            emulated__bluetooth__pb2.RawData.SerializeToString,
+            emulated__bluetooth__pb2.RawData.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def registerBlePhy(request_iterator,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.stream_stream(request_iterator, target, '/android.emulation.bluetooth.EmulatedBluetoothService/registerBlePhy',
+            emulated__bluetooth__pb2.RawData.SerializeToString,
+            emulated__bluetooth__pb2.RawData.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def registerHCIDevice(request_iterator,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.stream_stream(request_iterator, target, '/android.emulation.bluetooth.EmulatedBluetoothService/registerHCIDevice',
+            emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString,
+            emulated__bluetooth__packets__pb2.HCIPacket.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2.py b/bumble/transport/emulated_bluetooth_vhci_pb2.py
new file mode 100644
index 0000000..a638439
--- /dev/null
+++ b/bumble/transport/emulated_bluetooth_vhci_pb2.py
@@ -0,0 +1,43 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: emulated_bluetooth_vhci.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x65mulated_bluetooth_vhci.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto2y\n\x15VhciForwardingService\x12`\n\nattachVhci\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3')
+
+
+
+_VHCIFORWARDINGSERVICE = DESCRIPTOR.services_by_name['VhciForwardingService']
+if _descriptor._USE_C_DESCRIPTORS == False:
+
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\037com.android.emulation.bluetoothP\001\370\001\001\242\002\003AEB\252\002\033Android.Emulation.Bluetooth'
+  _VHCIFORWARDINGSERVICE._serialized_start=96
+  _VHCIFORWARDINGSERVICE._serialized_end=217
+# @@protoc_insertion_point(module_scope)
diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py b/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py
new file mode 100644
index 0000000..94140d7
--- /dev/null
+++ b/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py
@@ -0,0 +1,114 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+"""Client and server classes corresponding to protobuf-defined services."""
+import grpc
+
+from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2
+
+
+class VhciForwardingServiceStub(object):
+    """This is a service which allows you to directly intercept the VHCI packets
+    that are coming and going to the device before they are delivered to
+    the rootcanal service described below.
+
+    This service is usually not available on the emulator, and must be explictly
+    requested from the commandline.
+    """
+
+    def __init__(self, channel):
+        """Constructor.
+
+        Args:
+            channel: A grpc.Channel.
+        """
+        self.attachVhci = channel.stream_stream(
+                '/android.emulation.bluetooth.VhciForwardingService/attachVhci',
+                request_serializer=emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString,
+                response_deserializer=emulated__bluetooth__packets__pb2.HCIPacket.FromString,
+                )
+
+
+class VhciForwardingServiceServicer(object):
+    """This is a service which allows you to directly intercept the VHCI packets
+    that are coming and going to the device before they are delivered to
+    the rootcanal service described below.
+
+    This service is usually not available on the emulator, and must be explictly
+    requested from the commandline.
+    """
+
+    def attachVhci(self, request_iterator, context):
+        """This attach directly to /dev/vhci inside the android guest if available
+
+        - This will disable root canal.
+        - You will have to provide your own virtual bluetooth chip.
+
+        Some things to be aware of:
+        - Only one client can be active.
+        - Registering when bluetooth is active in android can result in
+        undefined behavior.
+        - If a client disconnects, rootcanal will be activated again
+
+        Status codes:
+        -  FAILED_PRECONDITION (code 9) If another client is controlling /dev/vhci.
+
+        This is an internal testing only interface, and is NOT publicly
+        supported.
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+
+def add_VhciForwardingServiceServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+            'attachVhci': grpc.stream_stream_rpc_method_handler(
+                    servicer.attachVhci,
+                    request_deserializer=emulated__bluetooth__packets__pb2.HCIPacket.FromString,
+                    response_serializer=emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString,
+            ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+            'android.emulation.bluetooth.VhciForwardingService', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+ # This class is part of an EXPERIMENTAL API.
+class VhciForwardingService(object):
+    """This is a service which allows you to directly intercept the VHCI packets
+    that are coming and going to the device before they are delivered to
+    the rootcanal service described below.
+
+    This service is usually not available on the emulator, and must be explictly
+    requested from the commandline.
+    """
+
+    @staticmethod
+    def attachVhci(request_iterator,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.stream_stream(request_iterator, target, '/android.emulation.bluetooth.VhciForwardingService/attachVhci',
+            emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString,
+            emulated__bluetooth__packets__pb2.HCIPacket.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
diff --git a/bumble/transport/file.py b/bumble/transport/file.py
new file mode 100644
index 0000000..841c62a
--- /dev/null
+++ b/bumble/transport/file.py
@@ -0,0 +1,60 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import io
+import logging
+
+from .common import Transport, StreamPacketSource, StreamPacketSink
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_file_transport(spec):
+    '''
+    Open a File transport (typically not for a real file, but for a PTY or other unix virtual files).
+    The parameter string is the path of the file to open
+    '''
+
+    # Open the file
+    file = io.open(spec, 'r+b', buffering=0)
+
+    # Setup reading
+    read_transport, packet_source = await asyncio.get_running_loop().connect_read_pipe(
+        lambda: StreamPacketSource(),
+        file
+    )
+
+    # Setup writing
+    write_transport, _ = await asyncio.get_running_loop().connect_write_pipe(
+        lambda: asyncio.BaseProtocol(),
+        file
+    )
+    packet_sink = StreamPacketSink(write_transport)
+
+    class FileTransport(Transport):
+        async def close(self):
+            read_transport.close()
+            write_transport.close()
+            file.close()
+
+    return FileTransport(packet_source, packet_sink)
+
diff --git a/bumble/transport/hci_socket.py b/bumble/transport/hci_socket.py
new file mode 100644
index 0000000..f74a535
--- /dev/null
+++ b/bumble/transport/hci_socket.py
@@ -0,0 +1,146 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import struct
+import os
+import socket
+import ctypes
+import collections
+
+from .common import Transport, ParserSource
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_hci_socket_transport(spec):
+    '''
+    Open an HCI Socket (only available on some platforms).
+    The parameter string is either empty (to use the first/default Bluetooth adapter)
+    or a 0-based integer to indicate the adapter number.
+    '''
+
+    HCI_CHANNEL_USER = 1
+
+    # Create a raw HCI socket
+    try:
+        hci_socket = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW | socket.SOCK_NONBLOCK, socket.BTPROTO_HCI)
+    except AttributeError:
+        # Not supported on this platform
+        logger.info("HCI sockets not supported on this platform")
+        raise Exception('Bluetooth HCI sockets not supported on this platform')
+
+    # Compute the adapter index
+    if spec is None:
+        adapter_index = 0
+    else:
+        adapter_index = int(spec)
+
+    # Bind the socket
+    # NOTE: since Python doesn't support binding with the required address format (yet),
+    # we need to go directly to the C runtime...
+    try:
+        ctypes.cdll.LoadLibrary('libc.so.6')
+        libc = ctypes.CDLL('libc.so.6', use_errno=True)
+    except OSError:
+        logger.info("HCI sockets not supported on this platform")
+        raise Exception('Bluetooth HCI sockets not supported on this platform')
+    libc.bind.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_char), ctypes.c_int)
+    libc.bind.restype = ctypes.c_int
+    bind_address = struct.pack('<HHH', socket.AF_BLUETOOTH, adapter_index, HCI_CHANNEL_USER)
+    if libc.bind(hci_socket.fileno(), ctypes.create_string_buffer(bind_address), len(bind_address)) != 0:
+        raise IOError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
+
+    class HciSocketSource(ParserSource):
+        def __init__(self, socket):
+            super().__init__()
+            self.socket  = socket
+            asyncio.get_running_loop().add_reader(socket.fileno(), self.recv_until_would_block)
+
+        def recv_until_would_block(self):
+            logger.debug('recv until would block +++')
+            while True:
+                try:
+                    packet = self.socket.recv(4096)
+                    logger.debug(f'received packet {len(packet)} bytes')
+                    self.parser.feed_data(packet)
+                except BlockingIOError:
+                    logger.debug('recv would block')
+                    break
+
+        def close(self):
+            asyncio.get_running_loop().remove_reader(self.socket.fileno())
+
+    class HciSocketSink:
+        def __init__(self, socket):
+            self.socket       = socket
+            self.packets      = collections.deque()
+            self.writer_added = False
+
+        def send_until_would_block(self):
+            logger.debug('send until would block ---')
+            while self.packets:
+                packet = self.packets.pop()
+                logger.debug('sending packet')
+                try:
+                    bytes_written = self.socket.send(packet)
+                except BlockingIOError:
+                    bytes_written = 0
+                if bytes_written != len(packet):
+                    # Note: we assume here that there are no partial writes
+                    logger.debug('send would block')
+                    break
+
+            if self.packets:
+                # There's still something to send, ensure that we are monitoring the socket
+                if not self.writer_added:
+                    asyncio.get_running_loop().add_writer(socket.fileno(), self.send_until_would_block)
+                    self.writer_added = True
+            else:
+                # Nothing left to send, stop monitoring the socket
+                if self.writer_added:
+                    asyncio.get_running_loop().remove_writer(self.socket.fileno())
+                    self.writer_added = False
+
+        def on_packet(self, packet):
+            self.packets.appendleft(packet)
+            self.send_until_would_block()
+
+        def close(self):
+            if self.writer_added:
+                asyncio.get_running_loop().remove_writer(self.socket.fileno())
+
+    class HciSocketTransport(Transport):
+        def __init__(self, socket, source, sink):
+            super().__init__(source, sink)
+            self.socket = socket
+
+        async def close(self):
+            logger.debug('closing HCI socket transport')
+            self.source.close()
+            self.sink.close()
+            self.socket.close()
+
+    packet_source = HciSocketSource(hci_socket)
+    packet_sink = HciSocketSink(hci_socket)
+    return HciSocketTransport(hci_socket, packet_source, packet_sink)
diff --git a/bumble/transport/pty.py b/bumble/transport/pty.py
new file mode 100644
index 0000000..2399d14
--- /dev/null
+++ b/bumble/transport/pty.py
@@ -0,0 +1,82 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import pty
+import tty
+import io
+import atexit
+import os
+import logging
+
+from .common import Transport, StreamPacketSource, StreamPacketSink
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_pty_transport(spec):
+    '''
+    Open a PTY transport.
+    The parameter string may be empty, or a path name where a symbolic link
+    to the PTY will be created (the link will be removed when the transport
+    is closed or when the process exits)
+    '''
+
+    primary, replica = pty.openpty()
+    replica_path = os.ttyname(replica)
+    logger.debug(f'pty open at {replica_path}')
+    tty.setraw(primary)
+    tty.setraw(replica)
+
+    read_transport, packet_source = await asyncio.get_running_loop().connect_read_pipe(
+        lambda: StreamPacketSource(),
+        io.open(primary, 'rb', closefd=False)
+    )
+
+    write_transport, _ = await asyncio.get_running_loop().connect_write_pipe(
+        lambda: asyncio.BaseProtocol(),
+        io.open(primary, 'wb', closefd=False)
+    )
+    packet_sink = StreamPacketSink(write_transport)
+
+    def cleanup():
+        if spec:
+            try:
+                os.unlink(spec)
+            except FileNotFoundError:
+                pass
+
+    # If required, create a symbolic link to the replica
+    # NOTE: the link will be removed when this process exits
+    if spec:
+        os.symlink(replica_path, spec)
+        logger.debug(f'linked pty at {spec}')
+        atexit.register(cleanup)
+
+    class PtyTransport(Transport):
+        async def close(self):
+            write_transport.close()
+            read_transport.close()
+            os.close(primary)
+            os.close(replica)
+            cleanup()
+
+    return PtyTransport(packet_source, packet_sink)
diff --git a/bumble/transport/pyusb.py b/bumble/transport/pyusb.py
new file mode 100644
index 0000000..ea5bc0d
--- /dev/null
+++ b/bumble/transport/pyusb.py
@@ -0,0 +1,276 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import usb.core
+import usb.util
+import threading
+import time
+from colors import color
+
+from .common import Transport, ParserSource
+from .. import hci
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_pyusb_transport(spec):
+    '''
+    Open a USB transport. [Implementation based on PyUSB]
+    The parameter string has this syntax:
+    either <index> or <vendor>:<product>
+    With <index> as the 0-based index to select amongst all the devices that appear
+    to be supporting Bluetooth HCI (0 being the first one), or
+    Where <vendor> and <product> are the vendor ID and product ID in hexadecimal.
+
+    Examples:
+    0 --> the first BT USB dongle
+    04b4:f901 --> the BT USB dongle with vendor=04b4 and product=f901
+    '''
+
+    USB_RECIPIENT_DEVICE                             = 0x00
+    USB_REQUEST_TYPE_CLASS                           = 0x01 << 5
+    USB_ENDPOINT_EVENTS_IN                           = 0x81
+    USB_ENDPOINT_ACL_IN                              = 0x82
+    USB_ENDPOINT_SCO_IN                              = 0x83
+    USB_ENDPOINT_ACL_OUT                             = 0x02
+    #  USB_ENDPOINT_SCO_OUT                             = 0x03
+    USB_DEVICE_CLASS_WIRELESS_CONTROLLER             = 0xE0
+    USB_DEVICE_SUBCLASS_RF_CONTROLLER                = 0x01
+    USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER = 0x01
+
+    READ_SIZE    = 1024
+    READ_TIMEOUT = 1000
+
+    class UsbPacketSink:
+        def __init__(self, device):
+            self.device     = device
+            self.thread     = threading.Thread(target=self.run)
+            self.loop       = asyncio.get_running_loop()
+            self.stop_event = None
+
+        def on_packet(self, packet):
+            # TODO: don't block here, just queue for the write thread
+            if len(packet) == 0:
+                logger.warning('packet too short')
+                return
+
+            packet_type = packet[0]
+            try:
+                if packet_type == hci.HCI_ACL_DATA_PACKET:
+                    self.device.write(USB_ENDPOINT_ACL_OUT, packet[1:])
+                elif packet_type == hci.HCI_COMMAND_PACKET:
+                    self.device.ctrl_transfer(USB_RECIPIENT_DEVICE | USB_REQUEST_TYPE_CLASS, 0, 0, 0, packet[1:])
+                else:
+                    logger.warning(color(f'unsupported packet type {packet_type}', 'red'))
+            except usb.core.USBTimeoutError:
+                logger.warning('USB Write Timeout')
+            except usb.core.USBError as error:
+                logger.warning(f'USB write error: {error}')
+                time.sleep(1)  # Sleep one second to avoid busy looping
+
+        def start(self):
+            self.thread.start()
+
+        async def stop(self):
+            # Create stop events and wait for them to be signaled
+            self.stop_event = asyncio.Event()
+            await self.stop_event.wait()
+
+        def run(self):
+            while self.stop_event is None:
+                time.sleep(1)
+            self.loop.call_soon_threadsafe(lambda: self.stop_event.set())
+
+    class UsbPacketSource(asyncio.Protocol, ParserSource):
+        def __init__(self, device, sco_enabled):
+            super().__init__()
+            self.device       = device
+            self.loop         = asyncio.get_running_loop()
+            self.queue        = asyncio.Queue()
+            self.event_thread = threading.Thread(
+                target=self.run,
+                args=(USB_ENDPOINT_EVENTS_IN, hci.HCI_EVENT_PACKET)
+            )
+            self.event_thread.stop_event = None
+            self.acl_thread = threading.Thread(
+                target=self.run,
+                args=(USB_ENDPOINT_ACL_IN, hci.HCI_ACL_DATA_PACKET)
+            )
+            self.acl_thread.stop_event = None
+
+            # SCO support is optional
+            self.sco_enabled = sco_enabled
+            if sco_enabled:
+                self.sco_thread = threading.Thread(
+                    target=self.run,
+                    args=(USB_ENDPOINT_SCO_IN, hci.HCI_SYNCHRONOUS_DATA_PACKET)
+                )
+                self.sco_thread.stop_event = None
+
+        def data_received(self, packet):
+            self.parser.feed_data(packet)
+
+        def enqueue(self, packet):
+            self.queue.put_nowait(packet)
+
+        async def dequeue(self):
+            while True:
+                try:
+                    data = await self.queue.get()
+                except asyncio.CancelledError:
+                    return
+                self.data_received(data)
+
+        def start(self):
+            self.dequeue_task = self.loop.create_task(self.dequeue())
+            self.event_thread.start()
+            self.acl_thread.start()
+            if self.sco_enabled:
+                self.sco_thread.start()
+
+        async def stop(self):
+            # Stop the dequeuing task
+            self.dequeue_task.cancel()
+
+            # Create stop events and wait for them to be signaled
+            self.event_thread.stop_event = asyncio.Event()
+            self.acl_thread.stop_event   = asyncio.Event()
+            await self.event_thread.stop_event.wait()
+            await self.acl_thread.stop_event.wait()
+            if self.sco_enabled:
+                await self.sco_thread.stop_event.wait()
+
+        def run(self, endpoint, packet_type):
+            # Read until asked to stop
+            current_thread = threading.current_thread()
+            while current_thread.stop_event is None:
+                try:
+                    # Read, with a timeout of 1 second
+                    data = self.device.read(endpoint, READ_SIZE, timeout=READ_TIMEOUT)
+                    packet = bytes([packet_type]) + data.tobytes()
+                    self.loop.call_soon_threadsafe(self.enqueue, packet)
+                except usb.core.USBTimeoutError:
+                    continue
+                except usb.core.USBError:
+                    # Don't log this: because pyusb doesn't really support multiple threads
+                    # reading at the same time, we can get occasional USBError(errno=5)
+                    # Input/Output errors reported, but they seem to be harmless.
+                    # Until support for async or multi-thread support is added to pyusb,
+                    # we'll just live with this as is...
+                    # logger.warning(f'USB read error: {error}')
+                    time.sleep(1)  # Sleep one second to avoid busy looping
+
+            stop_event = current_thread.stop_event
+            self.loop.call_soon_threadsafe(lambda: stop_event.set())
+
+    class UsbTransport(Transport):
+        def __init__(self, device, source, sink):
+            super().__init__(source, sink)
+            self.device = device
+
+        async def close(self):
+            await self.source.stop()
+            await self.sink.stop()
+            usb.util.release_interface(self.device, 0)
+
+    # Find the device according to the spec moniker
+    if ':' in spec:
+        vendor_id, product_id = spec.split(':')
+        device = usb.core.find(idVendor=int(vendor_id, 16), idProduct=int(product_id, 16))
+    else:
+        device_index = int(spec)
+        devices = list(usb.core.find(
+            find_all        = 1,
+            bDeviceClass    = USB_DEVICE_CLASS_WIRELESS_CONTROLLER,
+            bDeviceSubClass = USB_DEVICE_SUBCLASS_RF_CONTROLLER,
+            bDeviceProtocol = USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER
+        ))
+        if len(devices) > device_index:
+            device = devices[device_index]
+        else:
+            device = None
+
+    if device is None:
+        raise ValueError('device not found')
+    logger.debug(f'USB Device: {device}')
+
+    # Detach the kernel driver if needed
+    if device.is_kernel_driver_active(0):
+        logger.debug("detaching kernel driver")
+        device.detach_kernel_driver(0)
+
+    # Set configuration, if needed
+    try:
+        configuration = device.get_active_configuration()
+    except usb.core.USBError:
+        device.set_configuration()
+        configuration = device.get_active_configuration()
+    interface = configuration[(0, 0)]
+    logger.debug(f'USB Interface: {interface}')
+    usb.util.claim_interface(device, 0)
+
+    # Select an alternate setting for SCO, if available
+    sco_enabled = False
+    # NOTE: this is disabled for now, because SCO with alternate settings is broken,
+    # see: https://github.com/libusb/libusb/issues/36
+    #
+    # best_packet_size = 0
+    # best_interface = None
+    # sco_enabled = False
+    # for interface in configuration:
+    #     iso_in_endpoint = None
+    #     iso_out_endpoint = None
+    #     for endpoint in interface:
+    #         if (endpoint.bEndpointAddress == USB_ENDPOINT_SCO_IN and
+    #             usb.util.endpoint_direction(endpoint.bEndpointAddress) == usb.util.ENDPOINT_IN and
+    #             usb.util.endpoint_type(endpoint.bmAttributes) == usb.util.ENDPOINT_TYPE_ISO):
+    #             iso_in_endpoint = endpoint
+    #             continue
+    #         if (endpoint.bEndpointAddress == USB_ENDPOINT_SCO_OUT and
+    #             usb.util.endpoint_direction(endpoint.bEndpointAddress) == usb.util.ENDPOINT_OUT and
+    #             usb.util.endpoint_type(endpoint.bmAttributes) == usb.util.ENDPOINT_TYPE_ISO):
+    #             iso_out_endpoint = endpoint
+
+    #     if iso_in_endpoint is not None and iso_out_endpoint is not None:
+    #         if iso_out_endpoint.wMaxPacketSize > best_packet_size:
+    #             best_packet_size = iso_out_endpoint.wMaxPacketSize
+    #             best_interface = interface
+
+    # if best_interface is not None:
+    #     logger.debug(f'SCO enabled, selecting alternate setting (wMaxPacketSize={best_packet_size}): {best_interface}')
+    #     sco_enabled = True
+    #     try:
+    #         device.set_interface_altsetting(
+    #             interface = best_interface.bInterfaceNumber,
+    #             alternate_setting = best_interface.bAlternateSetting
+    #         )
+    #     except usb.USBError:
+    #         logger.warning('failed to set alternate setting')
+
+    packet_source = UsbPacketSource(device, sco_enabled)
+    packet_sink = UsbPacketSink(device)
+    packet_source.start()
+    packet_sink.start()
+
+    return UsbTransport(device, packet_source, packet_sink)
\ No newline at end of file
diff --git a/bumble/transport/serial.py b/bumble/transport/serial.py
new file mode 100644
index 0000000..b760a29
--- /dev/null
+++ b/bumble/transport/serial.py
@@ -0,0 +1,72 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import serial_asyncio
+
+from .common import Transport, StreamPacketSource, StreamPacketSink
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_serial_transport(spec):
+    '''
+    Open a serial port transport.
+    The parameter string has this syntax:
+    <device-path>[,<speed>][,rtscts][,dsrdtr]
+    When <speed> is omitted, the default value of 1000000 is used
+    When "rtscts" is specified, RTS/CTS hardware flow control is enabled
+    When "dsrdtr" is specified, DSR/DTR hardware flow control is enabled
+
+    Examples:
+    /dev/tty.usbmodem0006839912172
+    /dev/tty.usbmodem0006839912172,1000000
+    /dev/tty.usbmodem0006839912172,rtscts
+    '''
+
+    speed = 1000000
+    rtscts = False
+    dsrdtr = False
+    if ',' in spec:
+        parts = spec.split(',')
+        device = parts[0]
+        for part in parts[1:]:
+            if part == 'rtscts':
+                rtscts = True
+            elif part == 'dsrdtr':
+                dsrdtr = True
+            elif part.isnumeric():
+                speed = int(part)
+    else:
+        device = spec
+    serial_transport, packet_source = await serial_asyncio.create_serial_connection(
+        asyncio.get_running_loop(),
+        lambda: StreamPacketSource(),
+        device,
+        baudrate=speed,
+        rtscts=rtscts,
+        dsrdtr=dsrdtr
+    )
+    packet_sink = StreamPacketSink(serial_transport)
+
+    return Transport(packet_source, packet_sink)
+
diff --git a/bumble/transport/tcp_client.py b/bumble/transport/tcp_client.py
new file mode 100644
index 0000000..e250f25
--- /dev/null
+++ b/bumble/transport/tcp_client.py
@@ -0,0 +1,52 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+
+from .common import Transport, StreamPacketSource, StreamPacketSink
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_tcp_client_transport(spec):
+    '''
+    Open a TCP client transport.
+    The parameter string has this syntax:
+    <remote-host>:<remote-port>
+
+    Example: 127.0.0.1:9001
+    '''
+
+    class TcpPacketSource(StreamPacketSource):
+        def connection_lost(self, error):
+            logger.debug(f'connection lost: {error}')
+            self.terminated.set_result(error)
+
+    remote_host, remote_port = spec.split(':')
+    tcp_transport, packet_source = await asyncio.get_running_loop().create_connection(
+        lambda: TcpPacketSource(),
+        host=remote_host,
+        port=int(remote_port),
+    )
+    packet_sink = StreamPacketSink(tcp_transport)
+
+    return Transport(packet_source, packet_sink)
diff --git a/bumble/transport/tcp_server.py b/bumble/transport/tcp_server.py
new file mode 100644
index 0000000..6806683
--- /dev/null
+++ b/bumble/transport/tcp_server.py
@@ -0,0 +1,88 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+
+from .common import Transport, StreamPacketSource
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_tcp_server_transport(spec):
+    '''
+    Open a TCP server transport.
+    The parameter string has this syntax:
+    <local-host>:<local-port>
+    Where <local-host> may be the address of a local network interface, or '_'
+    to accept connections on all local network interfaces.
+
+    Example: _:9001
+    '''
+
+    class TcpServerTransport(Transport):
+        async def close(self):
+            await super().close()
+
+    class TcpServerProtocol:
+        def __init__(self, packet_source, packet_sink):
+            self.packet_source = packet_source
+            self.packet_sink   = packet_sink
+
+        # Called when a new connection is established
+        def connection_made(self, transport):
+            peername = transport.get_extra_info('peername')
+            logger.debug('connection from {}'.format(peername))
+            self.packet_sink.transport = transport
+
+        # Called when the client is disconnected
+        def connection_lost(self, error):
+            logger.debug(f'connection lost: {error}')
+            self.packet_sink.transport = None
+
+        def eof_received(self):
+            logger.debug('connection end')
+            self.packet_sink.transport = None
+
+        # Called when data is received on the socket
+        def data_received(self, data):
+            self.packet_source.data_received(data)
+
+    class TcpServerPacketSink:
+        def __init__(self):
+            self.transport = None
+
+        def on_packet(self, packet):
+            if self.transport:
+                self.transport.write(packet)
+            else:
+                logger.debug('no client, dropping packet')
+
+    local_host, local_port = spec.split(':')
+    packet_source = StreamPacketSource()
+    packet_sink   = TcpServerPacketSink()
+    await asyncio.get_running_loop().create_server(
+        lambda: TcpServerProtocol(packet_source, packet_sink),
+        host=local_host if local_host != '_' else None,
+        port=int(local_port),
+    )
+
+    return TcpServerTransport(packet_source, packet_sink)
diff --git a/bumble/transport/udp.py b/bumble/transport/udp.py
new file mode 100644
index 0000000..f4c59ea
--- /dev/null
+++ b/bumble/transport/udp.py
@@ -0,0 +1,63 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+
+from .common import Transport, ParserSource
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_udp_transport(spec):
+    '''
+    Open a UDP transport.
+    The parameter string has this syntax:
+    <local-host>:<local-port>,<remote-host>:<remote-port>
+
+    Example: 0.0.0.0:9000,127.0.0.1:9001
+    '''
+
+    class UdpPacketSource(asyncio.DatagramProtocol, ParserSource):
+        def datagram_received(self, data, addr):
+            self.parser.feed_data(data)
+
+    class UdpPacketSink:
+        def __init__(self, transport):
+            self.transport = transport
+
+        def on_packet(self, packet):
+            self.transport.sendto(packet)
+
+        def close(self):
+            self.transport.close()
+
+    local, remote = spec.split(',')
+    local_host, local_port = local.split(':')
+    remote_host, remote_port = remote.split(':')
+    udp_transport, packet_source = await asyncio.get_running_loop().create_datagram_endpoint(
+        lambda: UdpPacketSource(),
+        local_addr=(local_host, int(local_port)),
+        remote_addr=(remote_host, int(remote_port))
+    )
+    packet_sink = UdpPacketSink(udp_transport)
+
+    return Transport(packet_source, packet_sink)
diff --git a/bumble/transport/usb.py b/bumble/transport/usb.py
new file mode 100644
index 0000000..dfc169f
--- /dev/null
+++ b/bumble/transport/usb.py
@@ -0,0 +1,337 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import usb1
+import threading
+import collections
+from colors import color
+
+from .common import Transport, ParserSource
+from .. import hci
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_usb_transport(spec):
+    '''
+    Open a USB transport.
+    The parameter string has this syntax:
+    either <index> or <vendor>:<product>[/<serial-number>]
+    With <index> as the 0-based index to select amongst all the devices that appear
+    to be supporting Bluetooth HCI (0 being the first one), or
+    Where <vendor> and <product> are the vendor ID and product ID in hexadecimal. The
+    /<serial-number> suffix max be specified when more than one device with the same
+    vendor and product identifiers are present.
+
+    Examples:
+    0 --> the first BT USB dongle
+    04b4:f901 --> the BT USB dongle with vendor=04b4 and product=f901
+    04b4:f901/00E04C239987 --> the BT USB dongle with vendor=04b4 and product=f901 and serial number 00E04C239987
+    '''
+
+    USB_RECIPIENT_DEVICE                             = 0x00
+    USB_REQUEST_TYPE_CLASS                           = 0x01 << 5
+    USB_ENDPOINT_EVENTS_IN                           = 0x81
+    USB_ENDPOINT_ACL_IN                              = 0x82
+    USB_ENDPOINT_ACL_OUT                             = 0x02
+    USB_DEVICE_CLASS_WIRELESS_CONTROLLER             = 0xE0
+    USB_DEVICE_SUBCLASS_RF_CONTROLLER                = 0x01
+    USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER = 0x01
+
+    READ_SIZE = 1024
+
+    class UsbPacketSink:
+        def __init__(self, device):
+            self.device      = device
+            self.transfer    = device.getTransfer()
+            self.packets     = collections.deque()  # Queue of packets waiting to be sent
+            self.loop        = asyncio.get_running_loop()
+            self.cancel_done = self.loop.create_future()
+            self.closed      = False
+
+        def start(self):
+            pass
+
+        def on_packet(self, packet):
+            # Ignore packets if we're closed
+            if self.closed:
+                return
+
+            if len(packet) == 0:
+                logger.warning('packet too short')
+                return
+
+            # Queue the packet
+            self.packets.append(packet)
+            if len(self.packets) == 1:
+                # The queue was previously empty, re-prime the pump
+                self.process_queue()
+
+        def on_packet_sent(self, transfer):
+            status = transfer.getStatus()
+            # logger.debug(f'<<< USB out transfer callback: status={status}')
+
+            if status == usb1.TRANSFER_COMPLETED:
+                self.loop.call_soon_threadsafe(self.on_packet_sent_)
+            elif status == usb1.TRANSFER_CANCELLED:
+                self.loop.call_soon_threadsafe(self.cancel_done.set_result, None)
+            else:
+                logger.warning(color(f'!!! out transfer not completed: status={status}', 'red'))
+
+        def on_packet_sent_(self):
+            if self.packets:
+                self.packets.popleft()
+                self.process_queue()
+
+        def process_queue(self):
+            if len(self.packets) == 0:
+                return  # Nothing to do
+
+            packet = self.packets[0]
+            packet_type = packet[0]
+            if packet_type == hci.HCI_ACL_DATA_PACKET:
+                self.transfer.setBulk(
+                    USB_ENDPOINT_ACL_OUT,
+                    packet[1:],
+                    callback=self.on_packet_sent
+                )
+                logger.debug('submit ACL')
+                self.transfer.submit()
+            elif packet_type == hci.HCI_COMMAND_PACKET:
+                self.transfer.setControl(
+                    USB_RECIPIENT_DEVICE | USB_REQUEST_TYPE_CLASS, 0, 0, 0,
+                    packet[1:],
+                    callback=self.on_packet_sent
+                )
+                logger.debug('submit COMMAND')
+                self.transfer.submit()
+            else:
+                logger.warning(color(f'unsupported packet type {packet_type}', 'red'))
+
+        async def close(self):
+            self.closed = True
+
+            # Empty the packet queue so that we don't send any more data
+            self.packets.clear()
+
+            # If we have a transfer in flight, cancel it
+            if self.transfer.isSubmitted():
+                # Try to cancel the transfer, but that may fail because it may have already completed
+                try:
+                    self.transfer.cancel()
+
+                    logger.debug('waiting for OUT transfer cancellation to be done...')
+                    await self.cancel_done
+                    logger.debug('OUT transfer cancellation done')
+                except usb1.USBError:
+                    logger.debug('OUT transfer likely already completed')
+
+    class UsbPacketSource(asyncio.Protocol, ParserSource):
+        def __init__(self, context, device):
+            super().__init__()
+            self.context         = context
+            self.device          = device
+            self.loop            = asyncio.get_running_loop()
+            self.queue           = asyncio.Queue()
+            self.closed          = False
+            self.event_loop_done = self.loop.create_future()
+            self.cancel_done = {
+                hci.HCI_EVENT_PACKET:    self.loop.create_future(),
+                hci.HCI_ACL_DATA_PACKET: self.loop.create_future()
+            }
+
+            # Create a thread to process events
+            self.event_thread = threading.Thread(target=self.run)
+
+        def start(self):
+            # Set up transfer objects for input
+            self.events_in_transfer = device.getTransfer()
+            self.events_in_transfer.setInterrupt(
+                USB_ENDPOINT_EVENTS_IN,
+                READ_SIZE,
+                callback=self.on_packet_received,
+                user_data=hci.HCI_EVENT_PACKET
+            )
+            self.events_in_transfer.submit()
+
+            self.acl_in_transfer = device.getTransfer()
+            self.acl_in_transfer.setBulk(
+                USB_ENDPOINT_ACL_IN,
+                READ_SIZE,
+                callback=self.on_packet_received,
+                user_data=hci.HCI_ACL_DATA_PACKET
+            )
+            self.acl_in_transfer.submit()
+
+            self.dequeue_task = self.loop.create_task(self.dequeue())
+            self.event_thread.start()
+
+        def on_packet_received(self, transfer):
+            packet_type = transfer.getUserData()
+            status = transfer.getStatus()
+            # logger.debug(f'<<< USB IN transfer callback: status={status} packet_type={packet_type}')
+
+            if status == usb1.TRANSFER_COMPLETED:
+                packet = bytes([packet_type]) + transfer.getBuffer()[:transfer.getActualLength()]
+                self.loop.call_soon_threadsafe(self.queue.put_nowait, packet)
+            elif status == usb1.TRANSFER_CANCELLED:
+                self.loop.call_soon_threadsafe(self.cancel_done[packet_type].set_result, None)
+                return
+            else:
+                logger.warning(color(f'!!! transfer not completed: status={status}', 'red'))
+
+            # Re-submit the transfer so we can receive more data
+            transfer.submit()
+
+        async def dequeue(self):
+            while not self.closed:
+                try:
+                    packet = await self.queue.get()
+                except asyncio.CancelledError:
+                    return
+                self.parser.feed_data(packet)
+
+        def run(self):
+            logger.debug('starting USB event loop')
+            while self.events_in_transfer.isSubmitted() or self.acl_in_transfer.isSubmitted():
+                try:
+                    self.context.handleEvents()
+                except usb1.USBErrorInterrupted:
+                    pass
+
+            logger.debug('USB event loop done')
+            self.loop.call_soon_threadsafe(self.event_loop_done.set_result, None)
+
+        async def close(self):
+            self.closed = True
+            self.dequeue_task.cancel()
+
+            # Cancel the transfers
+            for transfer in (self.events_in_transfer, self.acl_in_transfer):
+                if transfer.isSubmitted():
+                    # Try to cancel the transfer, but that may fail because it may have already completed
+                    packet_type = transfer.getUserData()
+                    try:
+                        transfer.cancel()
+                        logger.debug(f'waiting for IN[{packet_type}] transfer cancellation to be done...')
+                        await self.cancel_done[packet_type]
+                        logger.debug(f'IN[{packet_type}] transfer cancellation done')
+                    except usb1.USBError:
+                        logger.debug(f'IN[{packet_type}] transfer likely already completed')
+
+            # Wait for the thread to terminate
+            await self.event_loop_done
+
+    class UsbTransport(Transport):
+        def __init__(self, context, device, interface, source, sink):
+            super().__init__(source, sink)
+            self.context   = context
+            self.device    = device
+            self.interface = interface
+
+            # Get exclusive access
+            device.claimInterface(interface)
+
+            # The source and sink can now start
+            source.start()
+            sink.start()
+
+        async def close(self):
+            await self.source.close()
+            await self.sink.close()
+            self.device.releaseInterface(self.interface)
+            self.device.close()
+            self.context.close()
+
+    # Find the device according to the spec moniker
+    context = usb1.USBContext()
+    context.open()
+    try:
+        found = None
+        if ':' in spec:
+            vendor_id, product_id = spec.split(':')
+            if '/' in product_id:
+                product_id, serial_number = product_id.split('/')
+                for device in context.getDeviceIterator(skip_on_error=True):
+                    if (
+                        device.getVendorID() == int(vendor_id, 16) and
+                        device.getProductID() == int(product_id, 16) and
+                        device.getSerialNumber() == serial_number
+                    ):
+                        found = device
+                        break
+                    device.close()
+            else:
+                found = context.getByVendorIDAndProductID(int(vendor_id, 16), int(product_id, 16), skip_on_error=True)
+        else:
+            device_index = int(spec)
+            for device in context.getDeviceIterator(skip_on_error=True):
+                if (
+                    device.getDeviceClass()    == USB_DEVICE_CLASS_WIRELESS_CONTROLLER and
+                    device.getDeviceSubClass() == USB_DEVICE_SUBCLASS_RF_CONTROLLER and
+                    device.getDeviceProtocol() == USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER
+                ):
+                    if device_index == 0:
+                        found = device
+                        break
+                    device_index -= 1
+                device.close()
+
+        if found is None:
+            context.close()
+            raise ValueError('device not found')
+
+        logger.debug(f'USB Device: {found}')
+        device = found.open()
+
+        # Set the configuration if needed
+        try:
+            configuration = device.getConfiguration()
+            logger.debug(f'current configuration = {configuration}')
+        except usb1.USBError:
+            try:
+                logger.debug('setting configuration 1')
+                device.setConfiguration(1)
+            except usb1.USBError:
+                logger.debug('failed to set configuration 1')
+
+        # Use the first interface
+        interface = 0
+
+        # Detach the kernel driver if supported and needed
+        if usb1.hasCapability(usb1.CAP_SUPPORTS_DETACH_KERNEL_DRIVER):
+            try:
+                if device.kernelDriverActive(interface):
+                    logger.debug("detaching kernel driver")
+                    device.detachKernelDriver(interface)
+            except usb1.USBError:
+                pass
+
+        source = UsbPacketSource(context, device)
+        sink   = UsbPacketSink(device)
+        return UsbTransport(context, device, interface, source, sink)
+    except usb1.USBError as error:
+        logger.warning(color(f'!!! failed to open USB device: {error}', 'red'))
+        context.close()
+        raise
diff --git a/bumble/transport/vhci.py b/bumble/transport/vhci.py
new file mode 100644
index 0000000..572c31d
--- /dev/null
+++ b/bumble/transport/vhci.py
@@ -0,0 +1,59 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+
+from .file import open_file_transport
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_vhci_transport(spec):
+    '''
+    Open a VHCI transport (only available on some platforms).
+    The parameter string is either empty (to use the default VHCI device
+    path at /dev/vhci), or the path of a VHCI device
+    '''
+
+    HCI_VENDOR_PKT = 0xff
+    HCI_BREDR = 0x00  # Controller type
+
+    # Open the VHCI device
+    transport = await open_file_transport(spec or '/dev/vhci')
+
+    # Override the source's `data_received` method so that we can
+    # filter out the vendor packet that is received just after the
+    # initial open
+    def vhci_data_received(data):
+        if len(data) > 0 and data[0] == HCI_VENDOR_PKT:
+            if len(data) == 4:
+                hci_index = data[2] << 8 | data[3]
+                logger.info(f'HCI index {hci_index}')
+        else:
+            transport.source.parser.feed_data(data)
+
+    transport.source.data_received = vhci_data_received
+
+    # Write the initial config
+    transport.sink.on_packet(bytes([HCI_VENDOR_PKT, HCI_BREDR]))
+
+    return transport
+
diff --git a/bumble/transport/ws_client.py b/bumble/transport/ws_client.py
new file mode 100644
index 0000000..9ee7e49
--- /dev/null
+++ b/bumble/transport/ws_client.py
@@ -0,0 +1,49 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import websockets
+
+from .common import PumpedPacketSource, PumpedPacketSink, PumpedTransport
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_ws_client_transport(spec):
+    '''
+    Open a WebSocket client transport.
+    The parameter string has this syntax:
+    <remote-host>:<remote-port>
+
+    Example: 127.0.0.1:9001
+    '''
+
+    remote_host, remote_port = spec.split(':')
+    uri = f'ws://{remote_host}:{remote_port}'
+    websocket = await websockets.connect(uri)
+
+    transport = PumpedTransport(
+        PumpedPacketSource(websocket.recv),
+        PumpedPacketSink(websocket.send),
+        websocket.close
+    )
+    transport.start()
+    return transport
diff --git a/bumble/transport/ws_server.py b/bumble/transport/ws_server.py
new file mode 100644
index 0000000..3b2d15e
--- /dev/null
+++ b/bumble/transport/ws_server.py
@@ -0,0 +1,81 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import websockets
+
+from .common import Transport, ParserSource, PumpedPacketSink
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+async def open_ws_server_transport(spec):
+    '''
+    Open a WebSocket server transport.
+    The parameter string has this syntax:
+    <local-host>:<local-port>
+    Where <local-host> may be the address of a local network interface, or '_'
+    to accept connections on all local network interfaces.
+
+    Example: _:9001
+    '''
+
+    class WsServerTransport(Transport):
+        def __init__(self):
+            source          = ParserSource()
+            sink            = PumpedPacketSink(self.send_packet)
+            self.connection = asyncio.get_running_loop().create_future()
+
+            super().__init__(source, sink)
+
+        async def serve(self, local_host, local_port):
+            self.sink.start()
+            self.server = await websockets.serve(
+                ws_handler = self.on_connection,
+                host       = local_host if local_host != '_' else None,
+                port       = int(local_port)
+            )
+            logger.debug(f'websocket server ready on port {local_port}')
+
+        async def on_connection(self, connection):
+            logger.debug(f'new connection on {connection.local_address} from {connection.remote_address}')
+            self.connection.set_result(connection)
+            try:
+                async for packet in connection:
+                    if type(packet) is bytes:
+                        self.source.parser.feed_data(packet)
+                    else:
+                        logger.warn('discarding packet: not a BINARY frame')
+            except websockets.WebSocketException as error:
+                logger.debug(f'exception while receiving packet: {error}')
+
+            # Wait for a new connection
+            self.connection = asyncio.get_running_loop().create_future()
+
+        async def send_packet(self, packet):
+            connection = await self.connection
+            return await connection.send(packet)
+
+    local_host, local_port = spec.split(':')
+    transport = WsServerTransport()
+    await transport.serve(local_host, local_port)
+    return transport
diff --git a/bumble/utils.py b/bumble/utils.py
new file mode 100644
index 0000000..1ab3fd7
--- /dev/null
+++ b/bumble/utils.py
@@ -0,0 +1,142 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import traceback
+from functools import wraps
+from colors import color
+from pyee import EventEmitter
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+def setup_event_forwarding(emitter, forwarder, event_name):
+    def emit(*args, **kwargs):
+        forwarder.emit(event_name, *args, **kwargs)
+    emitter.on(event_name, emit)
+
+
+# -----------------------------------------------------------------------------
+def composite_listener(cls):
+    """
+    Decorator that adds a `register` and `deregister` method to a class, which
+    registers/deregisters all methods named `on_<event_name>` as a listener for
+    the <event_name> event with an emitter.
+    """
+    def register(self, emitter):
+        for method_name in dir(cls):
+            if method_name.startswith('on_'):
+                emitter.on(method_name[3:], getattr(self, method_name))
+
+    def deregister(self, emitter):
+        for method_name in dir(cls):
+            if method_name.startswith('on_'):
+                emitter.remove_listener(method_name[3:], getattr(self, method_name))
+
+    cls._bumble_register_composite   = register
+    cls._bumble_deregister_composite = deregister
+    return cls
+
+
+# -----------------------------------------------------------------------------
+class CompositeEventEmitter(EventEmitter):
+    def __init__(self):
+        super().__init__()
+        self._listener = None
+
+    @property
+    def listener(self):
+        return self._listener
+
+    @listener.setter
+    def listener(self, listener):
+        if self._listener:
+            # Call the deregistration methods for each base class that has them
+            for cls in self._listener.__class__.mro():
+                if hasattr(cls, '_bumble_register_composite'):
+                    cls._bumble_deregister_composite(listener, self)
+        self._listener = listener
+        if listener:
+            # Call the registration methods for each base class that has them
+            for cls in listener.__class__.mro():
+                if hasattr(cls, '_bumble_deregister_composite'):
+                    cls._bumble_register_composite(listener, self)
+
+
+# -----------------------------------------------------------------------------
+class AsyncRunner:
+    class WorkQueue:
+        def __init__(self, create_task=True):
+            self.queue = None
+            self.task = None
+            self.create_task = create_task
+
+        def enqueue(self, coroutine):
+            # Create a task now if we need to and haven't done so already
+            if self.create_task and self.task is None:
+                self.task = asyncio.create_task(self.run())
+
+            # Lazy-create the coroutine queue
+            if self.queue is None:
+                self.queue = asyncio.Queue()
+
+            # Enqueue the work
+            self.queue.put_nowait(coroutine)
+
+        async def run(self):
+            while True:
+                item = await self.queue.get()
+                try:
+                    await item
+                except Exception as error:
+                    logger.warning(f'{color("!!! Exception in work queue:", "red")} {error}')
+
+    # Shared default queue
+    default_queue = WorkQueue()
+
+    @staticmethod
+    def run_in_task(queue=None):
+        """
+        Function decorator used to adapt an async function into a sync function
+        """
+
+        def decorator(func):
+            @wraps(func)
+            def wrapper(*args, **kwargs):
+                coroutine = func(*args, **kwargs)
+                if queue is None:
+                    # Create a task to run the coroutine
+                    async def run():
+                        try:
+                            await coroutine
+                        except Exception:
+                            logger.warning(f'{color("!!! Exception in wrapper:", "red")} {traceback.format_exc()}')
+
+                    asyncio.create_task(run())
+                else:
+                    # Queue the coroutine to be awaited by the work queue
+                    queue.enqueue(coroutine)
+
+            return wrapper
+
+        return decorator
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..ca7d4c0
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,22 @@
+Bumble Documentation
+====================
+
+The documentation consists of a collection of markdown text files, with the root of the file
+hierarchy at `docs/mkdocs/src`, starting with `docs/mkdocs/src/index.md`. 
+You can read the documentation as text, with any text viewer or your favorite markdown viewer,
+or generate a static HTML "site" using `mkdocs`, which you can then open with any browser.
+
+# Static HTML With MkDocs
+
+[MkDocs](https://www.mkdocs.org/) is used to generate a static HTML documentation site.
+The `mkdocs` directory contains all the data (actual documentation) and metadata (configuration) for the site.
+`mkdocs/requirements.txt` includes the list of Python packages needed to build the site.
+`mkdocs/mkdocs.yml` contains the site configuration.
+`mkdocs/src/` is the directory where the actual documentation text, in markdown format, is located.
+
+To build, from the project's root directory:  
+```
+$ mkdocs build -f docs/mkdocs/mkdocs.yml 
+```
+
+You can then open `docs/mkdocs/site/index.html` with any web browser.
diff --git a/docs/images/logo.png b/docs/images/logo.png
new file mode 100644
index 0000000..665d878
--- /dev/null
+++ b/docs/images/logo.png
Binary files differ
diff --git a/docs/images/logo.svg b/docs/images/logo.svg
new file mode 100644
index 0000000..28930e3
--- /dev/null
+++ b/docs/images/logo.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Vectornator for iOS (http://vectornator.io/) --><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg height="100%" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" xmlns:vectornator="http://vectornator.io" version="1.1" viewBox="0 0 600 600">
+<metadata>
+<vectornator:setting key="DimensionsVisible" value="1"/>
+<vectornator:setting key="PencilOnly" value="0"/>
+<vectornator:setting key="SnapToPoints" value="0"/>
+<vectornator:setting key="OutlineMode" value="0"/>
+<vectornator:setting key="CMYKEnabledKey" value="0"/>
+<vectornator:setting key="RulersVisible" value="1"/>
+<vectornator:setting key="SnapToEdges" value="0"/>
+<vectornator:setting key="GuidesVisible" value="1"/>
+<vectornator:setting key="DisplayWhiteBackground" value="0"/>
+<vectornator:setting key="doHistoryDisabled" value="0"/>
+<vectornator:setting key="SnapToGuides" value="1"/>
+<vectornator:setting key="TimeLapseWatermarkDisabled" value="0"/>
+<vectornator:setting key="Units" value="Pixels"/>
+<vectornator:setting key="DynamicGuides" value="0"/>
+<vectornator:setting key="IsolateActiveLayer" value="0"/>
+<vectornator:setting key="SnapToGrid" value="0"/>
+</metadata>
+<defs/>
+<g id="Layer 1" vectornator:layerName="Layer 1">
+<g opacity="1">
+<g opacity="1">
+<path stroke="#000000" stroke-width="20" d="M225.484+180.075L375.483+180.075L375.483+430.074L225.484+430.074L225.484+180.075Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="20" d="M225.484+180.075C225.484+138.654+259.063+105.076+300.484+105.076C341.905+105.076+375.483+138.654+375.483+180.075C375.483+221.496+341.905+255.075+300.484+255.075C259.063+255.075+225.484+221.496+225.484+180.075Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="20" d="M225.484+430.074C225.484+388.652+259.063+355.074+300.484+355.074C341.905+355.074+375.483+388.652+375.483+430.074C375.483+471.495+341.905+505.073+300.484+505.073C259.063+505.073+225.484+471.495+225.484+430.074Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#0082fc" stroke-width="0.1" d="M235.484+179.892L365.483+179.892L365.483+429.891L235.484+429.891L235.484+179.892Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+</g>
+<path stroke="#000000" stroke-width="0.1" d="M35.7722+199.986L159.567+334.722L299.282+200.423L35.7722+199.986Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="0.1" d="M316.022+197.895L441.498+330.932L583.111+198.326L316.022+197.895Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="20" d="M454.656+45.6273L162.17+339.701L16.4508+190.923L586.277+193.755L444.37+335.604L155.712+46.9328" fill="none" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="61.8698" d="M228.468+309.557L371.511+309.557" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="61.8698" d="M228.468+412.556L371.511+412.556" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<g opacity="1">
+<path stroke="#0082fc" stroke-width="0.1" d="M300.38+557.843L300.432+507.202L336.288+507.156C315.748+517.267+300.874+529.509+300.38+557.843Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#0082fc" stroke-width="0.1" d="M300.447+557.843L300.395+507.202L264.539+507.156C285.079+517.267+299.952+529.509+300.447+557.843Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+</g>
+</g>
+</g>
+</svg>
diff --git a/docs/images/logo.vectornator/Artboard0.json b/docs/images/logo.vectornator/Artboard0.json
new file mode 100644
index 0000000..eddf68a
--- /dev/null
+++ b/docs/images/logo.vectornator/Artboard0.json
@@ -0,0 +1 @@
+{"layers":[{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-5.882939210360405,25.513785542788241],"opacity":1,"blur":0,"isLocked":false,"gid":56,"smootheningRate":0,"initialPoint":[-5.882939210360405,25.513785542788241],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[225.48442074400293,105.07566425478205],"opacity":1,"blur":0,"isLocked":false,"gid":27,"smootheningRate":0,"initialPoint":[225.48442074400293,105.07566425478205],"creationPoints":[],"group":{"elements":[{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,180.0751680461143],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,180.0751680461143],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[225.48442074400293,180.0751680461143],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[375.4834254415108,180.0751680461143],"reflectionModeOverride":0,"anchorPoint":[375.4834254415108,180.0751680461143],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[375.4834254415108,180.0751680461143],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[375.4834254415108,430.07351680205772],"reflectionModeOverride":0,"anchorPoint":[375.4834254415108,430.07351680205772],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[375.4834254415108,430.07351680205772],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[225.48442074400293,430.07351680205772],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,430.07351680205772],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[225.48442074400293,430.07351680205772],"nextPoint":[-2.4497734478039774,-990.41770428277562]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[375.4834254415108,430.07351680205772],"opacity":1,"blur":0,"isLocked":false,"gid":22,"smootheningRate":0,"initialPoint":[225.48442074400293,180.0751680461143],"creationPoints":[],"name":"(rectangle)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,138.65412011223214],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,180.07518887113827],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[225.48442074400293,221.49625763004531],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[341.90501411926641,105.07566425478205],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,105.07566425478205],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[259.06287660145324,105.07566425478205],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[375.4834230115174,221.49625763004531],"reflectionModeOverride":0,"anchorPoint":[375.4834230115174,180.07518887113827],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[375.4834230115174,138.65412011223214],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[259.06287660145324,255.0746665222963],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,255.0746665222963],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[341.90501411926641,255.0746665222963],"nextPoint":[-235.35384472404451,-574.6867431949596]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-235.35384472404451,-574.6867431949596],"opacity":1,"blur":0,"isLocked":false,"gid":24,"smootheningRate":0,"initialPoint":[-235.35384472404451,-574.6867431949596],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,388.65246608333962],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,430.07353484224666],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[225.48442074400293,471.49460360115279],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[341.90501411926641,355.07401022588954],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,355.07401022588954],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[259.06287660145324,355.07401022588954],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[375.4834230115174,471.49460360115279],"reflectionModeOverride":0,"anchorPoint":[375.4834230115174,430.07353484224666],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[375.4834230115174,388.65246608333962],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[259.06287660145324,505.07301249340378],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,505.07301249340378],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[341.90501411926641,505.07301249340378],"nextPoint":[-235.35384472404451,-324.68839722385167]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-235.35384472404451,-324.68839722385167],"opacity":1,"blur":0,"isLocked":false,"gid":25,"smootheningRate":0,"initialPoint":[-235.35384472404451,-324.68839722385167],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[235.48435458284723,179.89237563632139],"reflectionModeOverride":0,"anchorPoint":[235.48435458284723,179.89237563632139],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[235.48435458284723,179.89237563632139],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[365.48349317943928,179.89237563632139],"reflectionModeOverride":0,"anchorPoint":[365.48349317943928,179.89237563632139],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[365.48349317943928,179.89237563632139],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[365.48349317943928,429.89072439226481],"reflectionModeOverride":0,"anchorPoint":[365.48349317943928,429.89072439226481],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[365.48349317943928,429.89072439226481],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[235.48435458284723,429.89072439226481],"reflectionModeOverride":0,"anchorPoint":[235.48435458284723,429.89072439226481],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[235.48435458284723,429.89072439226481],"nextPoint":[37.941384471822971,-990.60049669256784]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[365.48349317943928,429.89072439226481],"opacity":1,"blur":0,"isLocked":false,"gid":26,"smootheningRate":0,"initialPoint":[235.48435458284723,179.89237563632139],"creationPoints":[],"name":"(rectangle)"}]},"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[35.772159785141525,199.98637434514319],"reflectionModeOverride":0,"anchorPoint":[35.772159785141525,199.98637434514319],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[35.772159785141525,199.98637434514319],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[159.56661941832897,334.72204728763415],"reflectionModeOverride":0,"anchorPoint":[159.56661941832897,334.72204728763415],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[159.56661941832897,334.72204728763415],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[299.28226312627601,200.42272477566019],"reflectionModeOverride":0,"anchorPoint":[299.28226312627601,200.42272477566019],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[299.28226312627601,200.42272477566019],"nextPoint":[16.704525477792117,37.513706149401855]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,37.513706149401855],"opacity":1,"blur":0,"isLocked":false,"gid":29,"smootheningRate":0,"initialPoint":[16.704525477792117,37.513706149401855],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[316.02230657990947,197.89539868947611],"reflectionModeOverride":0,"anchorPoint":[316.02230657990947,197.89539868947611],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[316.02230657990947,197.89539868947611],"nextPoint":[296.695700377354,37.472065800512951]},{"outPoint":[441.49811195900963,330.9315945770885],"reflectionModeOverride":0,"anchorPoint":[441.49811195900963,330.9315945770885],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[441.49811195900963,330.9315945770885],"nextPoint":[296.695700377354,37.472065800512951]},{"outPoint":[583.11133899889126,198.32624525061453],"reflectionModeOverride":0,"anchorPoint":[583.11133899889126,198.32624525061453],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[583.11133899889126,198.32624525061453],"nextPoint":[296.695700377354,37.472065800512951]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[296.695700377354,37.472065800512951],"opacity":1,"blur":0,"isLocked":false,"gid":30,"smootheningRate":0,"initialPoint":[296.695700377354,37.472065800512951],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[454.65560248608688,45.627293553562936],"reflectionModeOverride":0,"anchorPoint":[454.65560248608688,45.627293553562936],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[454.65560248608688,45.627293553562936],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[162.17037499378046,339.70097131047794],"reflectionModeOverride":0,"anchorPoint":[162.17037499378046,339.70097131047794],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[162.17037499378046,339.70097131047794],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[16.450758016910754,190.92333427643769],"reflectionModeOverride":0,"anchorPoint":[16.450758016910754,190.92333427643769],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[16.450758016910754,190.92333427643769],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[586.27726302866802,193.75471648932478],"reflectionModeOverride":0,"anchorPoint":[586.27726302866802,193.75471648932478],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[586.27726302866802,193.75471648932478],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[444.36970577266027,335.60394507941498],"reflectionModeOverride":0,"anchorPoint":[444.36970577266027,335.60394507941498],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[444.36970577266027,335.60394507941498],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[155.71179749272039,46.932845447085015],"reflectionModeOverride":0,"anchorPoint":[155.71179749272039,46.932845447085015],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[155.71179749272039,46.932845447085015],"nextPoint":[971.93140466738657,-224.79785180257943]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":90,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[1132.6259120879768,-222.31407294998928],"opacity":1,"blur":0,"isLocked":false,"gid":21,"smootheningRate":0,"initialPoint":[1132.6259120879768,-222.31407294998928],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[228.4683004292159,309.55668062155007],"reflectionModeOverride":0,"anchorPoint":[228.4683004292159,309.55668062155007],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[228.4683004292159,309.55668062155007],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[371.51081106842264,309.55668062155007],"reflectionModeOverride":0,"anchorPoint":[371.51081106842264,309.55668062155007],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[371.51081106842264,309.55668062155007],"nextPoint":[16.704525477792117,37.513706149401855]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,37.513706149401855],"opacity":1,"blur":0,"isLocked":false,"gid":31,"smootheningRate":0,"initialPoint":[16.704525477792117,37.513706149401855],"creationPoints":[],"name":"(line)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[228.4683004292159,412.55599916164635],"reflectionModeOverride":0,"anchorPoint":[228.4683004292159,412.55599916164635],"cornerRadius":0,"prevPoint":[16.704525477792117,140.51302468949859],"inPoint":[228.4683004292159,412.55599916164635],"nextPoint":[16.704525477792117,140.51302468949859]},{"outPoint":[371.51081106842264,412.55599916164635],"reflectionModeOverride":0,"anchorPoint":[371.51081106842264,412.55599916164635],"cornerRadius":0,"prevPoint":[16.704525477792117,140.51302468949859],"inPoint":[371.51081106842264,412.55599916164635],"nextPoint":[16.704525477792117,140.51302468949859]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,140.51302468949859],"opacity":1,"blur":0,"isLocked":false,"gid":32,"smootheningRate":0,"initialPoint":[16.704525477792117,140.51302468949859],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[2.7206907461103356,25.513785542788241],"opacity":1,"blur":0,"isLocked":false,"gid":55,"smootheningRate":0,"initialPoint":[2.7206907461103356,25.513785542788241],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300.38008332937886,557.84279577418272],"reflectionModeOverride":0,"anchorPoint":[300.38008332937886,557.84279577418272],"cornerRadius":0,"prevPoint":[4.8049994992771872,-1.0129103054042616],"inPoint":[300.87435644597588,529.50877422158635],"nextPoint":[4.8049994992771872,-1.0129103054042616]},{"outPoint":[300.43161232869249,507.2021517541192],"reflectionModeOverride":0,"anchorPoint":[300.43161232869249,507.2021517541192],"cornerRadius":0,"prevPoint":[-12.365577443621419,-27.342871697380019],"inPoint":[300.43161232869249,507.2021517541192],"nextPoint":[-12.365577443621419,-27.342871697380019]},{"outPoint":[315.7481814828169,517.26662181075551],"reflectionModeOverride":0,"anchorPoint":[336.28774305214836,507.15563112854068],"cornerRadius":0,"prevPoint":[2.7206907461103356,24.978316814837285],"inPoint":[336.28774305214836,507.15563112854068],"nextPoint":[2.7206907461103356,24.978316814837285]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[4.9686415433105822,12.610834591390244],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[4.9686415433105822,12.610834591390244],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300.44667602585685,557.84279577418272],"reflectionModeOverride":0,"anchorPoint":[300.44667602585685,557.84279577418272],"cornerRadius":0,"prevPoint":[596.02175985595829,-1.0129103054042616],"inPoint":[299.95240290925983,529.50877422158635],"nextPoint":[596.02175985595829,-1.0129103054042616]},{"outPoint":[300.39514702654321,507.2021517541192],"reflectionModeOverride":0,"anchorPoint":[300.39514702654321,507.2021517541192],"cornerRadius":0,"prevPoint":[613.1923367988569,-27.342871697380019],"inPoint":[300.39514702654321,507.2021517541192],"nextPoint":[613.1923367988569,-27.342871697380019]},{"outPoint":[285.07857787241835,517.26662181075551],"reflectionModeOverride":0,"anchorPoint":[264.53901630308735,507.15563112854068],"cornerRadius":0,"prevPoint":[598.10606860912537,24.978316814837285],"inPoint":[264.53901630308735,507.15563112854068],"nextPoint":[598.10606860912537,24.978316814837285]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[595.85811781192467,12.818230389370797],"opacity":1,"blur":0,"isLocked":false,"gid":54,"smootheningRate":0,"initialPoint":[595.85811781192467,12.818230389370797],"creationPoints":[],"name":"(curve)"}]},"name":"(curve)"}]},"name":"(curve)"}],"isExpanded":false,"isLocked":false,"isVisible":true,"opacity":1,"gid":4,"name":"Layer 1"}],"frame":{"y":0,"x":0,"width":600,"height":600},"title":"Mac App icon","activeLayerIndex":0,"settings":{"gridSpacing":20,"gridAngle":45,"backgroundColor":{"b":1,"s":0,"h":0,"a":1},"gridMode":0,"isGridVisible":false},"guideLayer":{"isExpanded":false,"elements":[],"isLocked":false,"defaultName":"Guides","isVisible":true,"opacity":1,"name":"Guides","gid":5},"gid":3}
\ No newline at end of file
diff --git a/docs/images/logo.vectornator/Document.json b/docs/images/logo.vectornator/Document.json
new file mode 100644
index 0000000..ac7714f
--- /dev/null
+++ b/docs/images/logo.vectornator/Document.json
@@ -0,0 +1 @@
+{"date":644900643.85054696,"appVersion":"4.1.5","drawing":{"modificationDate":644894800.328192,"activeArtboardIndex":0,"settings":{"outlineMode":false,"isolateActiveLayer":false,"snapToEdges":false,"snapToPoints":false,"guidesVisible":true,"snapToGrid":false,"units":"Pixels","dimensionsVisible":true,"dynamicGuides":false,"isCMYKColorPreviewEnabled":false,"undoHistoryDisabled":false,"snapToGuides":true,"drawOnlyUsingPencil":false,"whiteBackground":false,"rulersVisible":true,"isTimeLapseWatermarkDisabled":false},"artboardPaths":["Artboard0.json"],"documentVersion":"unknown"}}
\ No newline at end of file
diff --git a/docs/images/logo.vectornator/Manifest.json b/docs/images/logo.vectornator/Manifest.json
new file mode 100644
index 0000000..0f80b78
--- /dev/null
+++ b/docs/images/logo.vectornator/Manifest.json
@@ -0,0 +1 @@
+{"documentJSONFilename":"Document.json","undoHistoryJSONFilename":"UndoHistory.json","fileFormatVersion":0,"thumbnailImageFilename":"Thumbnail.png"}
\ No newline at end of file
diff --git a/docs/images/logo.vectornator/Thumbnail.png b/docs/images/logo.vectornator/Thumbnail.png
new file mode 100644
index 0000000..5db5e38
--- /dev/null
+++ b/docs/images/logo.vectornator/Thumbnail.png
Binary files differ
diff --git a/docs/images/logo.vectornator/UndoHistory.json b/docs/images/logo.vectornator/UndoHistory.json
new file mode 100644
index 0000000..ead621d
--- /dev/null
+++ b/docs/images/logo.vectornator/UndoHistory.json
@@ -0,0 +1 @@
+{"cacheElements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[320,664.0506297595],"reflectionModeOverride":0,"anchorPoint":[320,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[320,580],"nextPoint":[0,0]},{"outPoint":[344.050612449646,595.89893977911322],"reflectionModeOverride":0,"anchorPoint":[337.34381007033926,612.48211711168926],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[327.23039949003834,637.48844227310212],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[360,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[352.025306224823,580],"nextPoint":[0,0]},{"outPoint":[260,580],"reflectionModeOverride":0,"anchorPoint":[280,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":true,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":34,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.67257107604084,316.56452785377144],"reflectionModeOverride":0,"anchorPoint":[221.67257107604084,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257107604084,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.78002147975192,316.58196755365179],"reflectionModeOverride":0,"anchorPoint":[222.50257105935154,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.50257105935154,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.46754319256974,316.15798118973765],"reflectionModeOverride":0,"anchorPoint":[223.27257099819641,316.3245278149393],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.05416436926282,316.49652037792839],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.56257095468965,315.05452779244973],"reflectionModeOverride":0,"anchorPoint":[223.56257095468965,315.65452781629159],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.57458948209043,315.9106673969701],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.50257101191011,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[222.50257101191011,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.18257095945802,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257102859941,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[221.67257102859941,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257102859941,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":7,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[223.39257110465107,318.92452774886726],"reflectionModeOverride":0,"anchorPoint":[223.39257110465107,318.92452774886726],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.39257110465107,318.92452774886726],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.33257116187153,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[222.33257116187153,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.33257116187153,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,314.29453168754219],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.85351610118363,314.2910670771638],"reflectionModeOverride":0,"anchorPoint":[222.64257116425571,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.64257116425571,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.44257068590508,314.46434348341825],"reflectionModeOverride":0,"anchorPoint":[223.26257121864279,314.39453162176318],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.06341253016012,314.32492145971571],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.8867055322342,314.82297152263595],"reflectionModeOverride":0,"anchorPoint":[223.75257125112995,314.69453165532451],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.60854628757448,314.56596123813961],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.15455094852729,315.31612333025106],"reflectionModeOverride":0,"anchorPoint":[224.07257117822328,315.14453165999419],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.99528738469252,314.97566482247669],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.19825083117667,315.9922372888982],"reflectionModeOverride":0,"anchorPoint":[224.19257122020886,315.69453168706491],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.19562660185056,315.50438689028209],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.7338072115908,316.75555373263109],"reflectionModeOverride":0,"anchorPoint":[223.91257119347284,316.51453168318102],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.09914577717603,316.28247388344346],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.19257120773227,317.01453168185151],"reflectionModeOverride":0,"anchorPoint":[223.19257120773227,317.01453168185151],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.48085346525613,316.9312160768892],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.99257120475204,317.08453168214953],"reflectionModeOverride":0,"anchorPoint":[222.99257120475204,317.08453168214953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.99257120475204,317.08453168214953],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.0925712285939,318.97453166784442],"reflectionModeOverride":0,"anchorPoint":[224.0925712285939,318.97453166784442],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.0925712285939,318.97453166784442],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":8,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.88165496610824,312.75214941635204],"reflectionModeOverride":0,"anchorPoint":[222.39257110465107,312.75452767257332],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257110465107,312.75452767257332],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.45378029794426,313.23854645592189],"reflectionModeOverride":0,"anchorPoint":[220.90257124445034,313.04452773196982],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.37529974656684,312.85070177480884],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.35876943156495,314.2208600575953],"reflectionModeOverride":0,"anchorPoint":[219.70257152159786,313.86452769906913],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.04640821090715,313.51691728007791],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.51848057480817,316.05954140545026],"reflectionModeOverride":0,"anchorPoint":[218.89257170245423,315.09452765536599],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.0841152815558,314.63792794299417],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.07885049250976,318.55391401613247],"reflectionModeOverride":0,"anchorPoint":[218.89257166857527,318.09452771720584],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.51848023468915,317.12951359608263],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.04640881460426,319.67213836278347],"reflectionModeOverride":0,"anchorPoint":[219.70257171508007,319.32452783266035],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.35412998721458,318.97193143333453],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.32887552639454,320.75534631866788],"reflectionModeOverride":0,"anchorPoint":[220.90257185775954,320.14452762388811],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.45378073087409,319.9505090873252],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.42747093525361,318.97720769048311],"reflectionModeOverride":0,"anchorPoint":[225.07257197531021,319.32452740981233],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.98375014972524,320.42992729161517],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25666363710593,317.12951355179308],"reflectionModeOverride":0,"anchorPoint":[225.8825720147413,318.09452742555698],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.70369149262439,318.55776181404121],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.69870344883279,314.63387871118448],"reflectionModeOverride":0,"anchorPoint":[225.88257204862026,315.09452736371713],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.25666342066654,316.05954129932087],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.73054756988128,313.51475021332715],"reflectionModeOverride":0,"anchorPoint":[225.07257201185996,313.86452742080394],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.42312528062655,314.21540813724465],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.40302999066401,312.851739523118],"reflectionModeOverride":0,"anchorPoint":[223.87257194075443,313.04452740721484],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.32272320141846,313.23607014436448],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.39257194847221,312.754527354262],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,312.754527354262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.90014945704277,312.7532021443493],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":9,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.79793545229796,321.06845412941806],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,321.06452777387869],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257194847221,321.06452777387869],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.13630955687603,320.47368746402753],"reflectionModeOverride":0,"anchorPoint":[220.66257205810552,320.71452796827054],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.20891937561655,320.94928924957026],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.84008207185138,319.31458146286332],"reflectionModeOverride":0,"anchorPoint":[219.26257159607349,319.71452784560978],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.66107353858615,320.13423302542299],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[217.79637500761001,317.20177600727493],"reflectionModeOverride":0,"anchorPoint":[218.26257166453621,318.30452834140976],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.50031051375157,318.83550383670706],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.71657507260346,313.79480682341318],"reflectionModeOverride":0,"anchorPoint":[218.26257150938892,314.85452883246262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[217.79637484257967,315.95728089190948],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.15964890699888,312.25319776201661],"reflectionModeOverride":0,"anchorPoint":[220.61257125382446,312.48452913388269],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.5567350056233,312.94749659064405],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.93948006388655,312.14028017121217],"reflectionModeOverride":0,"anchorPoint":[222.34257146546875,312.14452894709979],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.74863705025209,312.1374427227563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.61118094730551,312.72639164873863],"reflectionModeOverride":0,"anchorPoint":[224.08257153232208,312.48452915920495],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.53116831317226,312.25589748063879],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.90916737355408,313.88955714821628],"reflectionModeOverride":0,"anchorPoint":[225.49257153005306,313.48452928232075],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.08951592668194,313.06563634089633],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.95876793869749,315.99728178436908],"reflectionModeOverride":0,"anchorPoint":[226.49257128177129,314.89452945023424],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.24808810980261,314.36743571685838],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25070868333646,318.87313890226199],"reflectionModeOverride":0,"anchorPoint":[226.49257117287002,318.34452948727852],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.95876813673391,317.24177716378313],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.65955409863321,320.59420064219239],"reflectionModeOverride":0,"anchorPoint":[225.49257104975428,319.75452948500953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.91146402567489,319.35147381264608],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.34257072457945,321.06452950412722],"reflectionModeOverride":0,"anchorPoint":[222.34257072457945,321.06452950412722],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.52534831488276,321.06588609892162],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":10,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":6,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":1,"s":0,"h":0,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[189.49257363188801,253.97453151588081],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,253.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,253.97453151588081],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[198.97257317412434,244.48453174476265],"reflectionModeOverride":0,"anchorPoint":[198.97257317412434,244.48453174476265],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[198.97257317412434,244.48453174476265],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[189.49257363188801,235.01453147773384],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,235.01453147773384],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,235.01453147773384],"nextPoint":[112.85257138121662,152.97453151588078]}],"closed":true,"reversed":false}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,152.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":16,"smootheningRate":0,"initialPoint":[112.85257138121662,152.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300,640],"reflectionModeOverride":0,"anchorPoint":[300,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,640],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[340,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":36,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.2196979967656,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[270.70254252087443,481.42866614307741],"cornerRadius":0,"prevPoint":[0,1.3634276556741725],"inPoint":[246.45876716860445,474.78563603599895],"nextPoint":[0,1.3634276556741725]},{"outPoint":[308.32019835237998,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.80961189523731,532.13830093176216],"cornerRadius":0,"prevPoint":[9.9345758586960642,16.47175369678871],"inPoint":[307.29902543809465,503.80404406807986],"nextPoint":[9.9345758586960642,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[9.9345758586960642,15.108326041114537],"opacity":1,"blur":0,"isLocked":false,"gid":44,"smootheningRate":0,"initialPoint":[9.9345758586960642,15.108326041114537],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(polygon)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[368.75294547403814,729.44134214300266],"reflectionModeOverride":0,"anchorPoint":[368.75294547403814,729.44134214300266],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75294547403814,729.44134214300266],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884741476317231,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[58.884741476317231,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884741476317231,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884771993693448,192.73377248033557],"reflectionModeOverride":0,"anchorPoint":[58.884771993693448,192.73377248033557],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884771993693448,192.73377248033557],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[368.75296538097359,13.831330639116459],"reflectionModeOverride":0,"anchorPoint":[368.75296538097359,13.831330639116459],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75296538097359,13.831330639116459],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62124178681802,192.73392506721666],"reflectionModeOverride":0,"anchorPoint":[678.62124178681802,192.73392506721666],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62124178681802,192.73392506721666],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62118075206558,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[678.62118075206558,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62118075206558,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":0.3074892778331435},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":18.646402359008789,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[368.75296111419152,729.44134214300266],"opacity":1,"blur":0,"isLocked":false,"gid":57,"smootheningRate":0,"initialPoint":[368.75296111419152,371.63633639105956],"creationPoints":[],"name":"(polygon)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[340,700],"reflectionModeOverride":0,"anchorPoint":[340,660],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[340,620],"nextPoint":[0,0]},{"outPoint":[340,600],"reflectionModeOverride":0,"anchorPoint":[380,600],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[420,600],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":35,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[271.39702488697174,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[345.33038784778722,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,547.81167390718997],"reflectionModeOverride":0,"anchorPoint":[308.36370636737951,547.81167390718997],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,547.81167390718997],"nextPoint":[86.563617484933189,-170.37985441312253]}],"closed":true,"reversed":true}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[579.21777967228218,-165.50118646031109],"opacity":1,"blur":0,"isLocked":false,"gid":37,"smootheningRate":0,"initialPoint":[579.21777967228218,-165.50118646031109],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[13.974435927889729,160.80268978822852],"reflectionModeOverride":0,"anchorPoint":[13.974435927889729,160.80268978822852],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[13.974435927889729,160.80268978822852],"nextPoint":[0,0]},{"outPoint":[144.25556161242727,291.78142890790133],"reflectionModeOverride":0,"anchorPoint":[144.25556161242727,291.78142890790133],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[144.25556161242727,291.78142890790133],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":28,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[323.38754763944246,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[344.90470311533363,481.42866614307741],"cornerRadius":0,"prevPoint":[615.60724563620806,1.3634276556741725],"inPoint":[369.14847846760358,474.78563603599895],"nextPoint":[615.60724563620806,1.3634276556741725]},{"outPoint":[307.28704728382809,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.79763374097075,532.13830093176216],"cornerRadius":0,"prevPoint":[605.67266977751206,16.47175369678871],"inPoint":[308.30822019811342,503.80404406807986],"nextPoint":[605.67266977751206,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[605.67266977751206,16.47175369678871],"opacity":1,"blur":0,"isLocked":false,"gid":45,"smootheningRate":0,"initialPoint":[605.67266977751206,16.47175369678871],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[307.98765662154068,497.22766759942954],"reflectionModeOverride":0,"anchorPoint":[307.98765662154068,526.40806463936553],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[307.98765662154068,555.58846167930153],"nextPoint":[0,0]},{"outPoint":[347.07124677007465,468.5633046399771],"reflectionModeOverride":0,"anchorPoint":[331.56894097295356,478.9361833057668],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[316.06663517583246,489.3090619715565],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":33,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"}]}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-2.4740749822735211,26.734403557988003],"opacity":1,"blur":0,"isLocked":false,"gid":46,"smootheningRate":0,"initialPoint":[-2.4740749822735211,26.734403557988003],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-17.790763092914176,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[-17.790763092914176,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-17.790763092914176,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-118.14742861878335,355.38893101990755],"reflectionModeOverride":0,"anchorPoint":[-118.14742861878335,355.38893101990755],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-118.14742861878335,355.38893101990755],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-89.082653539453304,326.03057263672883],"reflectionModeOverride":0,"anchorPoint":[-89.082653539453304,326.03057263672883],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-89.082653539453304,326.03057263672883],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,406.22781863362536],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,406.22781863362536],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,406.22781863362536],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,214.66453204993843],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,214.66453204993843],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,214.66453204993843],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,359.98840044701058],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,359.98840044701058],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,359.98840044701058],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[40.289832016581158,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[40.289832016581158,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[40.289832016581158,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,551.89420905136228],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,551.89420905136228],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,551.89420905136228],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,697.21808678120522],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,697.21808678120522],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,697.21808678120522],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,505.65480953028907],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,505.65480953028907],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,505.65480953028907],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-88.78907023560464,585.60738760822585],"reflectionModeOverride":0,"anchorPoint":[-88.78907023560464,585.60738760822585],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-88.78907023560464,585.60738760822585],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-117.85384531493469,556.24902922504714],"reflectionModeOverride":0,"anchorPoint":[-117.85384531493469,556.24902922504714],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-117.85384531493469,556.24902922504714],"nextPoint":[-343.22817622315347,103.6410041510419]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-343.22817622315347,103.6410041510419],"opacity":1,"blur":0,"isLocked":false,"gid":13,"smootheningRate":0,"initialPoint":[-343.22817622315347,103.6410041510419],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-87.50742636811205,292.97453151588081],"reflectionModeOverride":0,"anchorPoint":[-87.50742636811205,292.97453151588081],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.50742636811205,292.97453151588081],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-78.027426825875665,283.48453174476265],"reflectionModeOverride":0,"anchorPoint":[-78.027426825875665,283.48453174476265],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-78.027426825875665,283.48453174476265],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-87.507426368111936,274.01453147773384],"reflectionModeOverride":0,"anchorPoint":[-87.507426368111936,274.01453147773384],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.507426368111936,274.01453147773384],"nextPoint":[-164.14742861878338,191.97453151588081]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588081],"opacity":1,"blur":0,"isLocked":false,"gid":14,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588081],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":11,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588078],"creationPoints":[],"name":"(curve)"}],"cacheLayers":[],"cacheArtboardPaths":[],"undoStack":[[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11,6]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -29.209948435277369, -232.41660739687057]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14,15]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.01450315572447696,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93007444840156717,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93451308072143913,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93910701763028559,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94055809677531532,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94999337125602745,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95297621548706413,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95790068402842177,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.011044777045815676,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96266052064980523,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0088583736096398309,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96911575815444173,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0077646546444650423,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97217928832645584,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0032726223185911016,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98016728703982747,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0010851843882415254,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98662352066719217,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99307006473711057,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.20437110010339629, -0, -0, 0.20437110010339629, 126.38769661378409, 170.79330548171168]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[13]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":2,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":186.05023255601679,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":558.85414169234264,\"OutPoint_X\":186.05023255601679,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":558.85414169234264},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":1,\"NodePoints\":{\"inPoint_X\":235.46249740746129,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":509.44413050490067,\"OutPoint_X\":136.6379677045723,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":608.26415287978466},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.03408561318607653,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.029926881951800856,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.021585238181938561,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.012961911164688478,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.011895648503707629,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.007872629590133573,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.005957393322960804,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-4.3711390001862419e-08, -0.99999999999999922, 0.99999999999999922, -4.3711390001862419e-08, -173.29768953296772, 744.95852622656867]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 8.2190355245806472, 176.90598115888827]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.55538688884063114, -0, 182.49084631957811]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 150.32620239929324, 36.081797695525552]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12249755187863798, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.64978701032960984, -0, -0, 0.64978701032960984, 52.531948450558538, 142.05406077476505]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.171276915352394, 196.69205141995428]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.65616231221472443, -0, -0, 0.65616231221472443, 51.425334356867019, 141.12771095949628]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.53221494113933, 129.60740108742715]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.17127691535239, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.09503537570964, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 208.92985571850454]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 130.84123782858848]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 75]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 4.5418914390142788, -306.56258907512347]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.96223302635797447, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0392493219135692, -0, -0, 1.0392493219135692, 0, -2.9436991435176969]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.458108560985721, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 26.562589075123469]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 50]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":22}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 9.881426012744555, 0.91199734232554874]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -77.745954011856824, 28.270796276850916]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 67.864527999112269, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.1538461432654479, -0, -0, 1, -1.5384614326544779, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -136.98574299882625, 7.8612196478959504]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -53.141264363887032, -43.604121250556744]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -70.052055171480333, -2.5049969673447094]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 219.77475934149726, 163.57319520938375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.6846814845770105, -0, -0, 0.6846814845770105, -0.080018108436871196, 3.1890182924268196]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -85.587957051898314, -21.453286888159028]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":142.86303913891427,\"AnchorPoint_Y\":297.21030751597522,\"inPoint_Y\":297.21030751597522,\"OutPoint_X\":142.86303913891427,\"AnchorPoint_X\":142.86303913891427,\"OutPoint_Y\":297.21030751597522},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -258.8309537310077, 14.790477788784642]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.98660023945785613, -0, -0, 1.0127745463822073, 7.5897658853896965, -3.7483052243093384]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":307.98765662154068,\"AnchorPoint_Y\":526.40806463936553,\"inPoint_Y\":555.58846167930153,\"OutPoint_X\":307.98765662154068,\"AnchorPoint_X\":307.98765662154068,\"OutPoint_Y\":497.22766759942954},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":9223372036854775807}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":580,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":680.00002878904479},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":660,\"inPoint_Y\":620,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":700},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 20, 20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 640, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 39, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":220,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 60, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{540, 0}\",\"VNPrevPointForRadiusKey\":\"{540, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":300,\"AnchorPoint_X\":280,\"OutPoint_Y\":600},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[],"methodSignature":"reversePathDirection","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":600,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":240,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -60, 100]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -8.3360061810856791, -0.18596575427056905]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12624915594960839, -0.84513498021431133]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0820554726071352, -0, -0, 1, -28.572516179113212, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 3.7693360211719664, 0.58007166688469169]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.8911271920692454, -0, 58.840421083655528]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0.018101184397323777, -6.1834757589472247]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.5616683932951787, -14.721591768371582]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.4335340527209723, 13.544422687729025]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":43}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.10440918421369,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":598.11903819000008},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":366.0442444460744,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.21462016330969},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":337.32713106884489,\"AnchorPoint_Y\":505.14299981511687,\"inPoint_Y\":505.14299981511687,\"OutPoint_X\":337.32713106884489,\"AnchorPoint_X\":337.32713106884489,\"OutPoint_Y\":505.14299981511687},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":282.38947067174871,\"AnchorPoint_Y\":502.68576551434728,\"inPoint_Y\":502.68576551434728,\"OutPoint_X\":282.38947067174871,\"AnchorPoint_X\":282.38947067174871,\"OutPoint_Y\":502.68576551434728},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"VNPrevPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":303.99512941310888,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":487.18557523329599,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":45}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 618.5121544161118, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -17.095091220096265, 18.636572344325828]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 2.4740749822735211, -26.734403557988003]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[46]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":50}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":50}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":323.12940602919167,\"AnchorPoint_Y\":534.54856008655031,\"inPoint_Y\":534.54856008655031,\"OutPoint_X\":323.12940602919167,\"AnchorPoint_X\":323.12940602919167,\"OutPoint_Y\":534.54856008655031},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[47,48]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":46},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13822251558303833,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -2.1531569701203352, 25.991399082341331]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":308.00218177519929,\"AnchorPoint_Y\":532.86800447564667,\"inPoint_Y\":504.53379546064855,\"OutPoint_X\":307.49158198784681,\"AnchorPoint_X\":307.49158198784681,\"OutPoint_Y\":532.86800447564667},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.54481307650724,\"AnchorPoint_Y\":482.22702540901258,\"inPoint_Y\":482.22702540901258,\"OutPoint_X\":307.54481307650724,\"AnchorPoint_X\":307.54481307650724,\"OutPoint_Y\":482.22702540901258},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"VNPrevPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":344.58533198784681,\"AnchorPoint_Y\":482.18050447564667,\"inPoint_Y\":482.18050447564667,\"OutPoint_X\":323.06817538141632,\"AnchorPoint_X\":344.58533198784681,\"OutPoint_Y\":488.07641427863712},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.34003453224391933]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.1954377384541317]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 692.07691397569363, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 57.024957634971202, 19.792602829855639]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":307.04977456552314,\"AnchorPoint_Y\":532.53992937509292,\"inPoint_Y\":504.20572036009486,\"OutPoint_X\":307.56037435287561,\"AnchorPoint_X\":307.56037435287561,\"OutPoint_Y\":532.53992937509292},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"VNPrevPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.50714326421519,\"AnchorPoint_Y\":481.89895030845889,\"inPoint_Y\":481.89895030845889,\"OutPoint_X\":307.50714326421519,\"AnchorPoint_X\":307.50714326421519,\"OutPoint_Y\":481.89895030845889},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"VNPrevPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":270.46662435287561,\"AnchorPoint_Y\":481.85242937509298,\"inPoint_Y\":481.85242937509298,\"OutPoint_X\":291.68464211968808,\"AnchorPoint_X\":270.46662435287561,\"OutPoint_Y\":491.96348695323337},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"VNPrevPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":55},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0330248439984473, -0, -0, 0.99999999999999989, -11.379876833051835, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[55]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":56},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":55}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -290.115478515625, 62.302734375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.89467432944640213,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.90097236067321962,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.031593452065677957,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.91739226661025597,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.019517801575741518,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93616537665754818,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0091232041181144082,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94328004740819738,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0018620895127118644,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95006183205443251,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95531556443573817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9587083584477003,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96669617604784863,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96977202191904677,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97283138648692502,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97731792325435829,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9818318081185089,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9863455118694362,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99094089768406901,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99553628349870182,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -138.240478515625, -358.288818359375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10.2197265625, 7.887451171875]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.73681640625, -1.3956298828125]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96954748896640086,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96362693977790437,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.95841712343394081,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.954131593465262,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.93934857275056949,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.83357840083997725,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.80253639308086555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.76741239856207288,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.7565612097807517,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.73508462058656032,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.69529915290432798,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.68285743522209563,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.66214385499715267,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.64970324957289294,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.63305608449601369,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.62061547907175396,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.60169597095671978,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.59555519468963558,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5870419721668565,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.57588046341116172,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.56733832218109337,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5457271497722096,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.51220146996013671,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.45939702270785876,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.41278562784738043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.38011749893223234,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.35006206399487472,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.33336373505125283,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31040673049544421,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29577720138097952,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.27499243664578588,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.25639993237471526,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24782776017938496,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23669294561503418,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22722206897779043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22344372864464693,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21991675861332574,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21874110193621868,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2197966347522779,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23113499252562641,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24180488325740318,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2529452591116173,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.28146132901480636,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29042946504840544,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29303437322038722,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30319262528473806,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31050016016514809,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31284924900341687,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31402379342255127,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31192273811218679,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31087054207004555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30975717183940776,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.3074892778331435,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0000066161593433, -0, -0, 1.0000066161593433, -0.00084115369224984499, -0.0015278888873493538]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 106.25150911801597, 215.10157288799655]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1024, 1024}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1018.1834573966, 1022.316502396953}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1008.5644489681886, 1015.9218488910001}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1003.3383988868811, 1012.188740268123}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {959.56182404754463, 968.37466824766148}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {930.99873639690077, 938.04865943448704}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {883.88375480634159, 898.86742705675647}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {851.39277451811745, 876.13749149913815}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {807.42735481491377, 847.80421878677203}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {780.8131139470363, 833.66058405713125}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {758.18676120653936, 823.6793880913026}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745.42543866806363, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.45307691385142, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.65313908147255}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {735.7456294412641, 820.1359296408807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {734.20024226104533, 819.105537302637}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {730.82720724054207, 815.73250228213374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {722.51078545309133, 807.41391622785272}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {715.22999050970566, 800.13291995732015}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {710.02528110597814, 794.3195482564854}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {708.99086222479468, 792.76802059828378}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {705.03881032957679, 786.83843280185465}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {703.71729893680003, 785.04591654871797}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {702.86608775936838, 784.19455437592615}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {701.16326275021129, 782.49188036212945}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {700.77188277648224, 782.10024872946656}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.97704320020512, 781.30550981676288}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.12865060283116, 780.87985389626033}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {698.70304501411556, 780.45414764397106}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {695.54100084364723, 776.8204946317021}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {693.70127337454369, 774.98056583545167}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.86235121402819, 772.67134345959084}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.97672115541332, 769.84279770809485}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.03491276184081, 768.90119064166925}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {686.67817717929256, 766.07264489017348}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {683.90469440249581, 763.76603976722345}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {682.53607245732564, 762.86897633206718}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {681.17026909221318, 762.39756881741346}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {678.95083862390538, 761.54620664462163}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.30580256688177, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.07408308165805, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.28025820206608, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {671.48603066818009, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.54341696601955, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.17600298373122, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.39103649860635, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.44842279644581, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.02281720772999, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.226367014277, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {663.57830298909857, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.7291050831368, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.92701772956843, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.54691207607016, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.69429082217562}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.30406847954168}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.91817467056762}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.52795232793369}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.12605301077474}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.73814593033103}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.96107347477482}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.97694686201953, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 757.18274272455005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 756.35493582795493}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.94518475206382}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.5517915068649}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.78614436689986}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.3960730196261}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.00237778370661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.60918586565458}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.21065877820774}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.81701387407497}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.41430924832821}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 749.69714774089903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 748.93285955917622}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.99004452986878}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.13868235707696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.41906127678772, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.65522608114543, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.27149653900142, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.88132452815421, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.48793128295529, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.69429966956, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.30453031300658, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.68503862079888, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.0695734715307, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.47263041978476, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.86843959074758, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.25498771294906, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.63670398362319, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.02566803158879, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.83158060094979, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {664.08383545516563, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.7862578100287, 746.7378903392264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.06307457617595, 747.16359659151567}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.70851328749336, 748.01490843252077}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.98533005364084, 748.44061468481004}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.66399580516031, 748.84437627807847}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.94081257130756, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.19387273411121, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.58525270784025, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.20474440004818, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.93235683113426, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.1381292972485, 747.52296554887494}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.87500277709569, 747.09725929658589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.47194582884163, 746.2471050866759}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.64368594616599, 745.41864387685314}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 745.02308636482417}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":330.67709661876142,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":247.83495910094825},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":214.25650324349817,\"inPoint_Y\":214.25650324349817,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":214.25650324349817},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":247.83495910094825,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":330.67709661876142},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":364.25550551101242,\"inPoint_Y\":364.25550551101242,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":364.25550551101242},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":580.67544258986891,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":497.83330507205574},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":464.25484921460566,\"inPoint_Y\":464.25484921460566,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":464.25484921460566},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":497.83330507205574,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":580.67544258986891},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":614.2538514821199,\"inPoint_Y\":614.2538514821199,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":614.2538514821199},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":97.57351080287799,\"AnchorPoint_Y\":309.16721333385931,\"inPoint_Y\":309.16721333385931,\"OutPoint_X\":97.57351080287799,\"AnchorPoint_X\":97.57351080287799,\"OutPoint_Y\":309.16721333385931},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":221.36797043606543,\"AnchorPoint_Y\":443.90288627635027,\"inPoint_Y\":443.90288627635027,\"OutPoint_X\":221.36797043606543,\"AnchorPoint_X\":221.36797043606543,\"OutPoint_Y\":443.90288627635027},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":361.08361414401247,\"AnchorPoint_Y\":309.6035637643763,\"inPoint_Y\":309.6035637643763,\"OutPoint_X\":361.08361414401247,\"AnchorPoint_X\":361.08361414401247,\"OutPoint_Y\":309.6035637643763},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":377.82365759764593,\"AnchorPoint_Y\":307.07623767819223,\"inPoint_Y\":307.07623767819223,\"OutPoint_X\":377.82365759764593,\"AnchorPoint_X\":377.82365759764593,\"OutPoint_Y\":307.07623767819223},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":503.2994629767461,\"AnchorPoint_Y\":440.11243356580462,\"inPoint_Y\":440.11243356580462,\"OutPoint_X\":503.2994629767461,\"AnchorPoint_X\":503.2994629767461,\"OutPoint_Y\":440.11243356580462},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":644.91269001662772,\"AnchorPoint_Y\":307.50708423933065,\"inPoint_Y\":307.50708423933065,\"OutPoint_X\":644.91269001662772,\"AnchorPoint_X\":644.91269001662772,\"OutPoint_Y\":307.50708423933065},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":516.45695350382334,\"AnchorPoint_Y\":154.80813254227905,\"inPoint_Y\":154.80813254227905,\"OutPoint_X\":516.45695350382334,\"AnchorPoint_X\":516.45695350382334,\"OutPoint_Y\":154.80813254227905},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":223.97172601151692,\"AnchorPoint_Y\":448.88181029919406,\"inPoint_Y\":448.88181029919406,\"OutPoint_X\":223.97172601151692,\"AnchorPoint_X\":223.97172601151692,\"OutPoint_Y\":448.88181029919406},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":78.252109034647219,\"AnchorPoint_Y\":300.1041732651538,\"inPoint_Y\":300.1041732651538,\"OutPoint_X\":78.252109034647219,\"AnchorPoint_X\":78.252109034647219,\"OutPoint_Y\":300.1041732651538},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":648.07861404640448,\"AnchorPoint_Y\":302.9355554780409,\"inPoint_Y\":302.9355554780409,\"OutPoint_X\":648.07861404640448,\"AnchorPoint_X\":648.07861404640448,\"OutPoint_Y\":302.9355554780409},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":506.17105679039673,\"AnchorPoint_Y\":444.7847840681311,\"inPoint_Y\":444.7847840681311,\"OutPoint_X\":506.17105679039673,\"AnchorPoint_X\":506.17105679039673,\"OutPoint_Y\":444.7847840681311},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":217.51314851045686,\"AnchorPoint_Y\":156.11368443580113,\"inPoint_Y\":156.11368443580113,\"OutPoint_X\":217.51314851045686,\"AnchorPoint_X\":217.51314851045686,\"OutPoint_Y\":156.11368443580113},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":362.67570746371234,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.18143434711533,\"AnchorPoint_X\":362.18143434711533,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.23296334642896,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.23296334642896,\"AnchorPoint_X\":362.23296334642896,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":398.08909406988482,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":377.54953250055337,\"AnchorPoint_X\":398.08909406988482,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":361.7537539269963,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.24802704359331,\"AnchorPoint_X\":362.24802704359331,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.19649804427968,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.19649804427968,\"AnchorPoint_X\":362.19649804427968,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":326.34036732082382,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":346.87992889015482,\"AnchorPoint_X\":326.34036732082382,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":\"Mac App icon\"}","argumentGID":0}],"methodSignature":"title","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 51.701018329683393, 116.18082292683278]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {740, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {736.35253901673036, 740.35218585345365}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {726.62032261171248, 732.07890244371663}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {706.29497285008392, 715.91557216276851}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.48129050959142, 702.64569380957585}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.51521301916046, 688.7168285032127}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.9666932377902, 683.82682021816697}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.51490451806353, 675.17444423582754}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.05376131978483, 661.73639796317264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {652.798107458392, 653.69283161018325}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {646.24074717873964, 645.29847372318682}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {639.28068335784974, 635.32950515648088}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.62597526532704, 630.02008897143548}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.24953610331818, 624.67241338007113}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 622.6021329778589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 621.53096597223009}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 620.05992231684661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 617.99269367248519}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 616.51850188795038}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 616.24683119169595}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 615.70516023506332}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 615.17627453763725}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.91966988803563}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.41860337270202}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.17012603611374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 613.92274090759827}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.49213479057289, 613.12141354351331}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.76223142225149, 612.8513490356014}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.57611917917507, 612.03746127867794}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.11913933401547, 611.49444112383765}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 610.95251317707016}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.88031821090215}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.63566360256891}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.14548704419769}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 605.7312446082625}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 599.34404392161696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 593.09060660013677}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 589.48773340525565}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.67320317299516}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.41560268662124}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.17014498411686}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 587.37090566487711}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 585.89687449917653}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.75570389792438, 583.82575887902624}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.35654683309554, 582.08108497761054}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 581.53800057523642}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 581.26632987898211}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 580.49002692927365}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 579.01760195191559}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 577.54289618711107}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 576.39787664149299}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 574.98324232069888}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.49819978285086, 574.14796013505304}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.68559697660157, 573.33526095750301}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.54147689645515, 572.22030925765694}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.72784612966643, 571.9486064376357}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {633.91472934314743, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {632.17002331796471, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {630.76017543843136, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {630.21689829345632, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {629.95939417838304, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {629.16606563224377, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.41930368598264, 572.55198715038807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.22070169725794, 573.15125602098351}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.65229090455068, 574.07519980313668}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.58506226018949, 575.54563310695005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.31316669756711, 575.81730380320448}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.51598329940589, 576.35817166566585}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.75935295706495, 576.11476988423976}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.01171726944222, 576.11476988423976}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.79887805234603, 575.59915130252296}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.61379376980881, 574.78423558506029}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.35849979499153, 574.21346049566114}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.4990220132504, 573.64088647531833}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 573.36995462570155}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 573.12317984875619}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.87576259647392}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.62320554149551}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.10858279655099}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 571.85628273170744}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.48437357556668, 571.58695707043307}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.96371156245436, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.71160424021173, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.46360876012614, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.21124444774887, 570.81418773507824}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.70805776380325, 570.81418773507824}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.18174196772497, 570.28806468160133}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.92449484278654, 570.01636186157998}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.11163504640217, 569.74465904155875}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.57632459560614, 569.20931646699569}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.31933446080234, 569.20931646699569}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.04743889817996, 568.93899496894903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.04743889817996, 568.68393226015633}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.80252729971198, 568.68393226015633}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.80252729971198, 570.51884182265508}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {600, 570.51884182265508}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.687962779094846, 1.6061883343354566e-05]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}]],"isGroupingUndo":false,"currentUndoGroup":[],"currentRedoGroup":[],"isGroupingRedo":false,"redoStack":[],"nextGID":57}
\ No newline at end of file
diff --git a/docs/images/logo_framed.png b/docs/images/logo_framed.png
new file mode 100644
index 0000000..d6704da
--- /dev/null
+++ b/docs/images/logo_framed.png
Binary files differ
diff --git a/docs/images/logo_framed.svg b/docs/images/logo_framed.svg
new file mode 100644
index 0000000..70ef7a9
--- /dev/null
+++ b/docs/images/logo_framed.svg
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Vectornator for iOS (http://vectornator.io/) --><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg height="100%" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" xmlns:vectornator="http://vectornator.io" version="1.1" viewBox="0 0 745 744.634">
+<metadata>
+<vectornator:setting key="DimensionsVisible" value="1"/>
+<vectornator:setting key="PencilOnly" value="0"/>
+<vectornator:setting key="SnapToPoints" value="0"/>
+<vectornator:setting key="OutlineMode" value="0"/>
+<vectornator:setting key="CMYKEnabledKey" value="0"/>
+<vectornator:setting key="RulersVisible" value="1"/>
+<vectornator:setting key="SnapToEdges" value="0"/>
+<vectornator:setting key="GuidesVisible" value="1"/>
+<vectornator:setting key="DisplayWhiteBackground" value="0"/>
+<vectornator:setting key="doHistoryDisabled" value="0"/>
+<vectornator:setting key="SnapToGuides" value="1"/>
+<vectornator:setting key="TimeLapseWatermarkDisabled" value="0"/>
+<vectornator:setting key="Units" value="Pixels"/>
+<vectornator:setting key="DynamicGuides" value="0"/>
+<vectornator:setting key="IsolateActiveLayer" value="0"/>
+<vectornator:setting key="SnapToGrid" value="0"/>
+</metadata>
+<defs/>
+<g id="Layer 1" vectornator:layerName="Layer 1">
+<path stroke="#000000" stroke-width="18.6464" d="M368.753+729.441L58.8847+550.539L58.8848+192.734L368.753+13.8313L678.621+192.734L678.621+550.539L368.753+729.441Z" fill="#0082fc" stroke-linecap="butt" fill-opacity="0.307489" opacity="1" stroke-linejoin="round"/>
+<g opacity="1">
+<g opacity="1">
+<path stroke="#000000" stroke-width="20" d="M292.873+289.256L442.872+289.256L442.872+539.254L292.873+539.254L292.873+289.256Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="20" d="M292.873+289.256C292.873+247.835+326.452+214.257+367.873+214.257C409.294+214.257+442.872+247.835+442.872+289.256C442.872+330.677+409.294+364.256+367.873+364.256C326.452+364.256+292.873+330.677+292.873+289.256Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="20" d="M292.873+539.254C292.873+497.833+326.452+464.255+367.873+464.255C409.294+464.255+442.872+497.833+442.872+539.254C442.872+580.675+409.294+614.254+367.873+614.254C326.452+614.254+292.873+580.675+292.873+539.254Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#0082fc" stroke-width="0.1" d="M302.873+289.073L432.872+289.073L432.872+539.072L302.873+539.072L302.873+289.073Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+</g>
+<path stroke="#000000" stroke-width="0.1" d="M103.161+309.167L226.956+443.903L366.671+309.604L103.161+309.167Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="0.1" d="M383.411+307.076L508.887+440.112L650.5+307.507L383.411+307.076Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="20" d="M522.045+154.808L229.559+448.882L83.8397+300.104L653.666+302.936L511.759+444.785L223.101+156.114" fill="none" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="61.8698" d="M295.857+418.738L438.9+418.738" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#000000" stroke-width="61.8698" d="M295.857+521.737L438.9+521.737" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<g opacity="1">
+<path stroke="#0082fc" stroke-width="0.1" d="M367.769+667.024L367.821+616.383L403.677+616.336C383.137+626.447+368.263+638.69+367.769+667.024Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+<path stroke="#0082fc" stroke-width="0.1" d="M367.836+667.024L367.784+616.383L331.928+616.336C352.468+626.447+367.341+638.69+367.836+667.024Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
+</g>
+</g>
+</g>
+</svg>
diff --git a/docs/images/logo_framed.vectornator/Artboard0.json b/docs/images/logo_framed.vectornator/Artboard0.json
new file mode 100644
index 0000000..7a365f7
--- /dev/null
+++ b/docs/images/logo_framed.vectornator/Artboard0.json
@@ -0,0 +1 @@
+{"layers":[{"elements":[{"elementDescription":"(polygon)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[368.75294547403814,729.44134214300266],"reflectionModeOverride":0,"anchorPoint":[368.75294547403814,729.44134214300266],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75294547403814,729.44134214300266],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884741476317231,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[58.884741476317231,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884741476317231,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884771993693448,192.73377248033557],"reflectionModeOverride":0,"anchorPoint":[58.884771993693448,192.73377248033557],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884771993693448,192.73377248033557],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[368.75296538097359,13.831330639116459],"reflectionModeOverride":0,"anchorPoint":[368.75296538097359,13.831330639116459],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75296538097359,13.831330639116459],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62124178681802,192.73392506721666],"reflectionModeOverride":0,"anchorPoint":[678.62124178681802,192.73392506721666],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62124178681802,192.73392506721666],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62118075206558,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[678.62118075206558,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62118075206558,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":0.3074892778331435},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":18.646402359008789,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[368.75296111419152,729.44134214300266],"opacity":1,"blur":0,"isLocked":false,"gid":57,"smootheningRate":0,"initialPoint":[368.75296111419152,371.63633639105956],"creationPoints":[],"name":"(polygon)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[61.506041898417834,134.69462453150436],"opacity":1,"blur":0,"isLocked":false,"gid":56,"smootheningRate":0,"initialPoint":[61.506041898417834,134.69462453150436],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[292.87340185278117,214.25650324349817],"opacity":1,"blur":0,"isLocked":false,"gid":27,"smootheningRate":0,"initialPoint":[292.87340185278117,214.25650324349817],"creationPoints":[],"group":{"elements":[{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,289.25600703483042],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,289.25600703483042],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[292.87340185278117,289.25600703483042],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[442.87240655028904,289.25600703483042],"reflectionModeOverride":0,"anchorPoint":[442.87240655028904,289.25600703483042],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[442.87240655028904,289.25600703483042],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[442.87240655028904,539.25435579077384],"reflectionModeOverride":0,"anchorPoint":[442.87240655028904,539.25435579077384],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[442.87240655028904,539.25435579077384],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[292.87340185278117,539.25435579077384],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,539.25435579077384],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[292.87340185278117,539.25435579077384],"nextPoint":[64.939207660974262,-881.2368652940595]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[442.87240655028904,539.25435579077384],"opacity":1,"blur":0,"isLocked":false,"gid":22,"smootheningRate":0,"initialPoint":[292.87340185278117,289.25600703483042],"creationPoints":[],"name":"(rectangle)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,247.83495910094825],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,289.25602785985438],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[292.87340185278117,330.67709661876142],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[409.29399522804465,214.25650324349817],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,214.25650324349817],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[326.45185771023148,214.25650324349817],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[442.87240412029564,330.67709661876142],"reflectionModeOverride":0,"anchorPoint":[442.87240412029564,289.25602785985438],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[442.87240412029564,247.83495910094825],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[326.45185771023148,364.25550551101242],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,364.25550551101242],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[409.29399522804465,364.25550551101242],"nextPoint":[-167.96486361526627,-465.50590420624349]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-167.96486361526627,-465.50590420624349],"opacity":1,"blur":0,"isLocked":false,"gid":24,"smootheningRate":0,"initialPoint":[-167.96486361526627,-465.50590420624349],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,497.83330507205574],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,539.25437383096278],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[292.87340185278117,580.67544258986891],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[409.29399522804465,464.25484921460566],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,464.25484921460566],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[326.45185771023148,464.25484921460566],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[442.87240412029564,580.67544258986891],"reflectionModeOverride":0,"anchorPoint":[442.87240412029564,539.25437383096278],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[442.87240412029564,497.83330507205574],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[326.45185771023148,614.2538514821199],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,614.2538514821199],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[409.29399522804465,614.2538514821199],"nextPoint":[-167.96486361526627,-215.50755823513555]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-167.96486361526627,-215.50755823513555],"opacity":1,"blur":0,"isLocked":false,"gid":25,"smootheningRate":0,"initialPoint":[-167.96486361526627,-215.50755823513555],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[302.87333569162547,289.07321462503751],"reflectionModeOverride":0,"anchorPoint":[302.87333569162547,289.07321462503751],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[302.87333569162547,289.07321462503751],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[432.87247428821752,289.07321462503751],"reflectionModeOverride":0,"anchorPoint":[432.87247428821752,289.07321462503751],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[432.87247428821752,289.07321462503751],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[432.87247428821752,539.07156338098093],"reflectionModeOverride":0,"anchorPoint":[432.87247428821752,539.07156338098093],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[432.87247428821752,539.07156338098093],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[302.87333569162547,539.07156338098093],"reflectionModeOverride":0,"anchorPoint":[302.87333569162547,539.07156338098093],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[302.87333569162547,539.07156338098093],"nextPoint":[105.33036558060121,-881.41965770385173]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[432.87247428821752,539.07156338098093],"opacity":1,"blur":0,"isLocked":false,"gid":26,"smootheningRate":0,"initialPoint":[302.87333569162547,289.07321462503751],"creationPoints":[],"name":"(rectangle)"}]},"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[103.16114089391976,309.16721333385931],"reflectionModeOverride":0,"anchorPoint":[103.16114089391976,309.16721333385931],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[103.16114089391976,309.16721333385931],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[226.95560052710721,443.90288627635027],"reflectionModeOverride":0,"anchorPoint":[226.95560052710721,443.90288627635027],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[226.95560052710721,443.90288627635027],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[366.67124423505425,309.6035637643763],"reflectionModeOverride":0,"anchorPoint":[366.67124423505425,309.6035637643763],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[366.67124423505425,309.6035637643763],"nextPoint":[84.093506586570356,146.69454513811797]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,146.69454513811797],"opacity":1,"blur":0,"isLocked":false,"gid":29,"smootheningRate":0,"initialPoint":[84.093506586570356,146.69454513811797],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[383.41128768868771,307.07623767819223],"reflectionModeOverride":0,"anchorPoint":[383.41128768868771,307.07623767819223],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[383.41128768868771,307.07623767819223],"nextPoint":[364.08468148613224,146.65290478922907]},{"outPoint":[508.88709306778787,440.11243356580462],"reflectionModeOverride":0,"anchorPoint":[508.88709306778787,440.11243356580462],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[508.88709306778787,440.11243356580462],"nextPoint":[364.08468148613224,146.65290478922907]},{"outPoint":[650.50032010766949,307.50708423933065],"reflectionModeOverride":0,"anchorPoint":[650.50032010766949,307.50708423933065],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[650.50032010766949,307.50708423933065],"nextPoint":[364.08468148613224,146.65290478922907]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[364.08468148613224,146.65290478922907],"opacity":1,"blur":0,"isLocked":false,"gid":30,"smootheningRate":0,"initialPoint":[364.08468148613224,146.65290478922907],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[522.04458359486512,154.80813254227905],"reflectionModeOverride":0,"anchorPoint":[522.04458359486512,154.80813254227905],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[522.04458359486512,154.80813254227905],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[229.5593561025587,448.88181029919406],"reflectionModeOverride":0,"anchorPoint":[229.5593561025587,448.88181029919406],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[229.5593561025587,448.88181029919406],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[83.839739125688993,300.1041732651538],"reflectionModeOverride":0,"anchorPoint":[83.839739125688993,300.1041732651538],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[83.839739125688993,300.1041732651538],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[653.66624413744626,302.9355554780409],"reflectionModeOverride":0,"anchorPoint":[653.66624413744626,302.9355554780409],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[653.66624413744626,302.9355554780409],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[511.75868688143851,444.7847840681311],"reflectionModeOverride":0,"anchorPoint":[511.75868688143851,444.7847840681311],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[511.75868688143851,444.7847840681311],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[223.10077860149863,156.11368443580113],"reflectionModeOverride":0,"anchorPoint":[223.10077860149863,156.11368443580113],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[223.10077860149863,156.11368443580113],"nextPoint":[1039.3203857761648,-115.61701281386331]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":90,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[1200.0148931967549,-113.13323396127316],"opacity":1,"blur":0,"isLocked":false,"gid":21,"smootheningRate":0,"initialPoint":[1200.0148931967549,-113.13323396127316],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[295.85728153799414,418.73751961026619],"reflectionModeOverride":0,"anchorPoint":[295.85728153799414,418.73751961026619],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[295.85728153799414,418.73751961026619],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[438.89979217720088,418.73751961026619],"reflectionModeOverride":0,"anchorPoint":[438.89979217720088,418.73751961026619],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[438.89979217720088,418.73751961026619],"nextPoint":[84.093506586570356,146.69454513811797]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,146.69454513811797],"opacity":1,"blur":0,"isLocked":false,"gid":31,"smootheningRate":0,"initialPoint":[84.093506586570356,146.69454513811797],"creationPoints":[],"name":"(line)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[295.85728153799414,521.73683815036247],"reflectionModeOverride":0,"anchorPoint":[295.85728153799414,521.73683815036247],"cornerRadius":0,"prevPoint":[84.093506586570356,249.69386367821471],"inPoint":[295.85728153799414,521.73683815036247],"nextPoint":[84.093506586570356,249.69386367821471]},{"outPoint":[438.89979217720088,521.73683815036247],"reflectionModeOverride":0,"anchorPoint":[438.89979217720088,521.73683815036247],"cornerRadius":0,"prevPoint":[84.093506586570356,249.69386367821471],"inPoint":[438.89979217720088,521.73683815036247],"nextPoint":[84.093506586570356,249.69386367821471]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,249.69386367821471],"opacity":1,"blur":0,"isLocked":false,"gid":32,"smootheningRate":0,"initialPoint":[84.093506586570356,249.69386367821471],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[70.109671854888575,134.69462453150436],"opacity":1,"blur":0,"isLocked":false,"gid":55,"smootheningRate":0,"initialPoint":[70.109671854888575,134.69462453150436],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.83565713463508,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.83565713463508,667.02363476289884],"cornerRadius":0,"prevPoint":[663.41074096473653,108.16792868331186],"inPoint":[367.34138401803807,638.68961321030247],"nextPoint":[663.41074096473653,108.16792868331186]},{"outPoint":[367.78412813532145,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.78412813532145,616.38299074283532],"cornerRadius":0,"prevPoint":[680.58131790763514,81.837967291336099],"inPoint":[367.78412813532145,616.38299074283532],"nextPoint":[680.58131790763514,81.837967291336099]},{"outPoint":[352.46755898119659,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[331.92799741186559,616.33647011725679],"cornerRadius":0,"prevPoint":[665.49504971790361,134.1591558035534],"inPoint":[331.92799741186559,616.33647011725679],"nextPoint":[665.49504971790361,134.1591558035534]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[663.24709892070291,121.99906937808692],"opacity":1,"blur":0,"isLocked":false,"gid":54,"smootheningRate":0,"initialPoint":[663.24709892070291,121.99906937808692],"creationPoints":[],"name":"(curve)"}]},"name":"(curve)"}]},"name":"(curve)"}],"isExpanded":false,"isLocked":false,"isVisible":true,"opacity":1,"gid":4,"name":"Layer 1"}],"frame":{"y":0,"x":0,"width":745,"height":744.6337699943515},"title":"Mac App icon","activeLayerIndex":0,"settings":{"gridSpacing":20,"gridAngle":45,"backgroundColor":{"b":1,"s":0,"h":0,"a":1},"gridMode":0,"isGridVisible":false},"guideLayer":{"isExpanded":false,"elements":[],"isLocked":false,"defaultName":"Guides","isVisible":true,"opacity":1,"name":"Guides","gid":5},"gid":3}
\ No newline at end of file
diff --git a/docs/images/logo_framed.vectornator/Document.json b/docs/images/logo_framed.vectornator/Document.json
new file mode 100644
index 0000000..974df2c
--- /dev/null
+++ b/docs/images/logo_framed.vectornator/Document.json
@@ -0,0 +1 @@
+{"date":644900741.09290397,"appVersion":"4.1.5","drawing":{"modificationDate":644894800.328192,"activeArtboardIndex":0,"settings":{"outlineMode":false,"isolateActiveLayer":false,"snapToEdges":false,"snapToPoints":false,"guidesVisible":true,"snapToGrid":false,"units":"Pixels","dimensionsVisible":true,"dynamicGuides":false,"isCMYKColorPreviewEnabled":false,"undoHistoryDisabled":false,"snapToGuides":true,"drawOnlyUsingPencil":false,"whiteBackground":false,"rulersVisible":true,"isTimeLapseWatermarkDisabled":false},"artboardPaths":["Artboard0.json"],"documentVersion":"unknown"}}
\ No newline at end of file
diff --git a/docs/images/logo_framed.vectornator/Manifest.json b/docs/images/logo_framed.vectornator/Manifest.json
new file mode 100644
index 0000000..0f80b78
--- /dev/null
+++ b/docs/images/logo_framed.vectornator/Manifest.json
@@ -0,0 +1 @@
+{"documentJSONFilename":"Document.json","undoHistoryJSONFilename":"UndoHistory.json","fileFormatVersion":0,"thumbnailImageFilename":"Thumbnail.png"}
\ No newline at end of file
diff --git a/docs/images/logo_framed.vectornator/Thumbnail.png b/docs/images/logo_framed.vectornator/Thumbnail.png
new file mode 100644
index 0000000..e3eb759
--- /dev/null
+++ b/docs/images/logo_framed.vectornator/Thumbnail.png
Binary files differ
diff --git a/docs/images/logo_framed.vectornator/UndoHistory.json b/docs/images/logo_framed.vectornator/UndoHistory.json
new file mode 100644
index 0000000..0238dd1
--- /dev/null
+++ b/docs/images/logo_framed.vectornator/UndoHistory.json
@@ -0,0 +1 @@
+{"cacheElements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-17.790763092914176,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[-17.790763092914176,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-17.790763092914176,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-118.14742861878335,355.38893101990755],"reflectionModeOverride":0,"anchorPoint":[-118.14742861878335,355.38893101990755],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-118.14742861878335,355.38893101990755],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-89.082653539453304,326.03057263672883],"reflectionModeOverride":0,"anchorPoint":[-89.082653539453304,326.03057263672883],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-89.082653539453304,326.03057263672883],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,406.22781863362536],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,406.22781863362536],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,406.22781863362536],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,214.66453204993843],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,214.66453204993843],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,214.66453204993843],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,359.98840044701058],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,359.98840044701058],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,359.98840044701058],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[40.289832016581158,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[40.289832016581158,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[40.289832016581158,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,551.89420905136228],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,551.89420905136228],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,551.89420905136228],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,697.21808678120522],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,697.21808678120522],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,697.21808678120522],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,505.65480953028907],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,505.65480953028907],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,505.65480953028907],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-88.78907023560464,585.60738760822585],"reflectionModeOverride":0,"anchorPoint":[-88.78907023560464,585.60738760822585],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-88.78907023560464,585.60738760822585],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-117.85384531493469,556.24902922504714],"reflectionModeOverride":0,"anchorPoint":[-117.85384531493469,556.24902922504714],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-117.85384531493469,556.24902922504714],"nextPoint":[-343.22817622315347,103.6410041510419]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-343.22817622315347,103.6410041510419],"opacity":1,"blur":0,"isLocked":false,"gid":13,"smootheningRate":0,"initialPoint":[-343.22817622315347,103.6410041510419],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-87.50742636811205,292.97453151588081],"reflectionModeOverride":0,"anchorPoint":[-87.50742636811205,292.97453151588081],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.50742636811205,292.97453151588081],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-78.027426825875665,283.48453174476265],"reflectionModeOverride":0,"anchorPoint":[-78.027426825875665,283.48453174476265],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-78.027426825875665,283.48453174476265],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-87.507426368111936,274.01453147773384],"reflectionModeOverride":0,"anchorPoint":[-87.507426368111936,274.01453147773384],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.507426368111936,274.01453147773384],"nextPoint":[-164.14742861878338,191.97453151588081]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588081],"opacity":1,"blur":0,"isLocked":false,"gid":14,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588081],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":11,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[340,700],"reflectionModeOverride":0,"anchorPoint":[340,660],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[340,620],"nextPoint":[0,0]},{"outPoint":[340,600],"reflectionModeOverride":0,"anchorPoint":[380,600],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[420,600],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":35,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.67257107604084,316.56452785377144],"reflectionModeOverride":0,"anchorPoint":[221.67257107604084,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257107604084,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.78002147975192,316.58196755365179],"reflectionModeOverride":0,"anchorPoint":[222.50257105935154,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.50257105935154,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.46754319256974,316.15798118973765],"reflectionModeOverride":0,"anchorPoint":[223.27257099819641,316.3245278149393],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.05416436926282,316.49652037792839],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.56257095468965,315.05452779244973],"reflectionModeOverride":0,"anchorPoint":[223.56257095468965,315.65452781629159],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.57458948209043,315.9106673969701],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.50257101191011,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[222.50257101191011,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.18257095945802,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257102859941,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[221.67257102859941,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257102859941,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":7,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[223.39257110465107,318.92452774886726],"reflectionModeOverride":0,"anchorPoint":[223.39257110465107,318.92452774886726],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.39257110465107,318.92452774886726],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.33257116187153,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[222.33257116187153,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.33257116187153,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,314.29453168754219],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.85351610118363,314.2910670771638],"reflectionModeOverride":0,"anchorPoint":[222.64257116425571,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.64257116425571,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.44257068590508,314.46434348341825],"reflectionModeOverride":0,"anchorPoint":[223.26257121864279,314.39453162176318],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.06341253016012,314.32492145971571],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.8867055322342,314.82297152263595],"reflectionModeOverride":0,"anchorPoint":[223.75257125112995,314.69453165532451],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.60854628757448,314.56596123813961],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.15455094852729,315.31612333025106],"reflectionModeOverride":0,"anchorPoint":[224.07257117822328,315.14453165999419],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.99528738469252,314.97566482247669],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.19825083117667,315.9922372888982],"reflectionModeOverride":0,"anchorPoint":[224.19257122020886,315.69453168706491],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.19562660185056,315.50438689028209],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.7338072115908,316.75555373263109],"reflectionModeOverride":0,"anchorPoint":[223.91257119347284,316.51453168318102],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.09914577717603,316.28247388344346],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.19257120773227,317.01453168185151],"reflectionModeOverride":0,"anchorPoint":[223.19257120773227,317.01453168185151],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.48085346525613,316.9312160768892],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.99257120475204,317.08453168214953],"reflectionModeOverride":0,"anchorPoint":[222.99257120475204,317.08453168214953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.99257120475204,317.08453168214953],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.0925712285939,318.97453166784442],"reflectionModeOverride":0,"anchorPoint":[224.0925712285939,318.97453166784442],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.0925712285939,318.97453166784442],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":8,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.88165496610824,312.75214941635204],"reflectionModeOverride":0,"anchorPoint":[222.39257110465107,312.75452767257332],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257110465107,312.75452767257332],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.45378029794426,313.23854645592189],"reflectionModeOverride":0,"anchorPoint":[220.90257124445034,313.04452773196982],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.37529974656684,312.85070177480884],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.35876943156495,314.2208600575953],"reflectionModeOverride":0,"anchorPoint":[219.70257152159786,313.86452769906913],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.04640821090715,313.51691728007791],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.51848057480817,316.05954140545026],"reflectionModeOverride":0,"anchorPoint":[218.89257170245423,315.09452765536599],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.0841152815558,314.63792794299417],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.07885049250976,318.55391401613247],"reflectionModeOverride":0,"anchorPoint":[218.89257166857527,318.09452771720584],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.51848023468915,317.12951359608263],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.04640881460426,319.67213836278347],"reflectionModeOverride":0,"anchorPoint":[219.70257171508007,319.32452783266035],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.35412998721458,318.97193143333453],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.32887552639454,320.75534631866788],"reflectionModeOverride":0,"anchorPoint":[220.90257185775954,320.14452762388811],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.45378073087409,319.9505090873252],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.42747093525361,318.97720769048311],"reflectionModeOverride":0,"anchorPoint":[225.07257197531021,319.32452740981233],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.98375014972524,320.42992729161517],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25666363710593,317.12951355179308],"reflectionModeOverride":0,"anchorPoint":[225.8825720147413,318.09452742555698],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.70369149262439,318.55776181404121],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.69870344883279,314.63387871118448],"reflectionModeOverride":0,"anchorPoint":[225.88257204862026,315.09452736371713],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.25666342066654,316.05954129932087],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.73054756988128,313.51475021332715],"reflectionModeOverride":0,"anchorPoint":[225.07257201185996,313.86452742080394],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.42312528062655,314.21540813724465],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.40302999066401,312.851739523118],"reflectionModeOverride":0,"anchorPoint":[223.87257194075443,313.04452740721484],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.32272320141846,313.23607014436448],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.39257194847221,312.754527354262],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,312.754527354262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.90014945704277,312.7532021443493],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":9,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.79793545229796,321.06845412941806],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,321.06452777387869],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257194847221,321.06452777387869],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.13630955687603,320.47368746402753],"reflectionModeOverride":0,"anchorPoint":[220.66257205810552,320.71452796827054],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.20891937561655,320.94928924957026],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.84008207185138,319.31458146286332],"reflectionModeOverride":0,"anchorPoint":[219.26257159607349,319.71452784560978],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.66107353858615,320.13423302542299],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[217.79637500761001,317.20177600727493],"reflectionModeOverride":0,"anchorPoint":[218.26257166453621,318.30452834140976],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.50031051375157,318.83550383670706],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.71657507260346,313.79480682341318],"reflectionModeOverride":0,"anchorPoint":[218.26257150938892,314.85452883246262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[217.79637484257967,315.95728089190948],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.15964890699888,312.25319776201661],"reflectionModeOverride":0,"anchorPoint":[220.61257125382446,312.48452913388269],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.5567350056233,312.94749659064405],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.93948006388655,312.14028017121217],"reflectionModeOverride":0,"anchorPoint":[222.34257146546875,312.14452894709979],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.74863705025209,312.1374427227563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.61118094730551,312.72639164873863],"reflectionModeOverride":0,"anchorPoint":[224.08257153232208,312.48452915920495],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.53116831317226,312.25589748063879],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.90916737355408,313.88955714821628],"reflectionModeOverride":0,"anchorPoint":[225.49257153005306,313.48452928232075],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.08951592668194,313.06563634089633],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.95876793869749,315.99728178436908],"reflectionModeOverride":0,"anchorPoint":[226.49257128177129,314.89452945023424],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.24808810980261,314.36743571685838],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25070868333646,318.87313890226199],"reflectionModeOverride":0,"anchorPoint":[226.49257117287002,318.34452948727852],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.95876813673391,317.24177716378313],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.65955409863321,320.59420064219239],"reflectionModeOverride":0,"anchorPoint":[225.49257104975428,319.75452948500953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.91146402567489,319.35147381264608],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.34257072457945,321.06452950412722],"reflectionModeOverride":0,"anchorPoint":[222.34257072457945,321.06452950412722],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.52534831488276,321.06588609892162],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":10,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":6,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.2196979967656,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[270.70254252087443,481.42866614307741],"cornerRadius":0,"prevPoint":[0,1.3634276556741725],"inPoint":[246.45876716860445,474.78563603599895],"nextPoint":[0,1.3634276556741725]},{"outPoint":[308.32019835237998,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.80961189523731,532.13830093176216],"cornerRadius":0,"prevPoint":[9.9345758586960642,16.47175369678871],"inPoint":[307.29902543809465,503.80404406807986],"nextPoint":[9.9345758586960642,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[9.9345758586960642,15.108326041114537],"opacity":1,"blur":0,"isLocked":false,"gid":44,"smootheningRate":0,"initialPoint":[9.9345758586960642,15.108326041114537],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[271.39702488697174,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[345.33038784778722,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,547.81167390718997],"reflectionModeOverride":0,"anchorPoint":[308.36370636737951,547.81167390718997],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,547.81167390718997],"nextPoint":[86.563617484933189,-170.37985441312253]}],"closed":true,"reversed":true}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[579.21777967228218,-165.50118646031109],"opacity":1,"blur":0,"isLocked":false,"gid":37,"smootheningRate":0,"initialPoint":[579.21777967228218,-165.50118646031109],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[307.98765662154068,497.22766759942954],"reflectionModeOverride":0,"anchorPoint":[307.98765662154068,526.40806463936553],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[307.98765662154068,555.58846167930153],"nextPoint":[0,0]},{"outPoint":[347.07124677007465,468.5633046399771],"reflectionModeOverride":0,"anchorPoint":[331.56894097295356,478.9361833057668],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[316.06663517583246,489.3090619715565],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":33,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"}]}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-2.4740749822735211,26.734403557988003],"opacity":1,"blur":0,"isLocked":false,"gid":46,"smootheningRate":0,"initialPoint":[-2.4740749822735211,26.734403557988003],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":1,"s":0,"h":0,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[189.49257363188801,253.97453151588081],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,253.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,253.97453151588081],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[198.97257317412434,244.48453174476265],"reflectionModeOverride":0,"anchorPoint":[198.97257317412434,244.48453174476265],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[198.97257317412434,244.48453174476265],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[189.49257363188801,235.01453147773384],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,235.01453147773384],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,235.01453147773384],"nextPoint":[112.85257138121662,152.97453151588078]}],"closed":true,"reversed":false}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,152.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":16,"smootheningRate":0,"initialPoint":[112.85257138121662,152.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[323.38754763944246,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[344.90470311533363,481.42866614307741],"cornerRadius":0,"prevPoint":[615.60724563620806,1.3634276556741725],"inPoint":[369.14847846760358,474.78563603599895],"nextPoint":[615.60724563620806,1.3634276556741725]},{"outPoint":[307.28704728382809,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.79763374097075,532.13830093176216],"cornerRadius":0,"prevPoint":[605.67266977751206,16.47175369678871],"inPoint":[308.30822019811342,503.80404406807986],"nextPoint":[605.67266977751206,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[605.67266977751206,16.47175369678871],"opacity":1,"blur":0,"isLocked":false,"gid":45,"smootheningRate":0,"initialPoint":[605.67266977751206,16.47175369678871],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[13.974435927889729,160.80268978822852],"reflectionModeOverride":0,"anchorPoint":[13.974435927889729,160.80268978822852],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[13.974435927889729,160.80268978822852],"nextPoint":[0,0]},{"outPoint":[144.25556161242727,291.78142890790133],"reflectionModeOverride":0,"anchorPoint":[144.25556161242727,291.78142890790133],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[144.25556161242727,291.78142890790133],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":28,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[320,664.0506297595],"reflectionModeOverride":0,"anchorPoint":[320,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[320,580],"nextPoint":[0,0]},{"outPoint":[344.050612449646,595.89893977911322],"reflectionModeOverride":0,"anchorPoint":[337.34381007033926,612.48211711168926],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[327.23039949003834,637.48844227310212],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[360,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[352.025306224823,580],"nextPoint":[0,0]},{"outPoint":[260,580],"reflectionModeOverride":0,"anchorPoint":[280,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":true,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":34,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300,640],"reflectionModeOverride":0,"anchorPoint":[300,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,640],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[340,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":36,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"}],"cacheLayers":[],"cacheArtboardPaths":[],"undoStack":[[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11,6]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -29.209948435277369, -232.41660739687057]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14,15]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.01450315572447696,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93007444840156717,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93451308072143913,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93910701763028559,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94055809677531532,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94999337125602745,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95297621548706413,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95790068402842177,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.011044777045815676,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96266052064980523,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0088583736096398309,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96911575815444173,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0077646546444650423,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97217928832645584,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0032726223185911016,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98016728703982747,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0010851843882415254,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98662352066719217,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99307006473711057,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.20437110010339629, -0, -0, 0.20437110010339629, 126.38769661378409, 170.79330548171168]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[13]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":2,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":186.05023255601679,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":558.85414169234264,\"OutPoint_X\":186.05023255601679,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":558.85414169234264},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":1,\"NodePoints\":{\"inPoint_X\":235.46249740746129,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":509.44413050490067,\"OutPoint_X\":136.6379677045723,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":608.26415287978466},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.03408561318607653,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.029926881951800856,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.021585238181938561,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.012961911164688478,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.011895648503707629,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.007872629590133573,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.005957393322960804,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-4.3711390001862419e-08, -0.99999999999999922, 0.99999999999999922, -4.3711390001862419e-08, -173.29768953296772, 744.95852622656867]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 8.2190355245806472, 176.90598115888827]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.55538688884063114, -0, 182.49084631957811]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 150.32620239929324, 36.081797695525552]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12249755187863798, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.64978701032960984, -0, -0, 0.64978701032960984, 52.531948450558538, 142.05406077476505]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.171276915352394, 196.69205141995428]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.65616231221472443, -0, -0, 0.65616231221472443, 51.425334356867019, 141.12771095949628]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.53221494113933, 129.60740108742715]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.17127691535239, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.09503537570964, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 208.92985571850454]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 130.84123782858848]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 75]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 4.5418914390142788, -306.56258907512347]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.96223302635797447, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0392493219135692, -0, -0, 1.0392493219135692, 0, -2.9436991435176969]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.458108560985721, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 26.562589075123469]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 50]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":22}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 9.881426012744555, 0.91199734232554874]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -77.745954011856824, 28.270796276850916]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 67.864527999112269, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.1538461432654479, -0, -0, 1, -1.5384614326544779, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -136.98574299882625, 7.8612196478959504]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -53.141264363887032, -43.604121250556744]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -70.052055171480333, -2.5049969673447094]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 219.77475934149726, 163.57319520938375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.6846814845770105, -0, -0, 0.6846814845770105, -0.080018108436871196, 3.1890182924268196]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -85.587957051898314, -21.453286888159028]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":142.86303913891427,\"AnchorPoint_Y\":297.21030751597522,\"inPoint_Y\":297.21030751597522,\"OutPoint_X\":142.86303913891427,\"AnchorPoint_X\":142.86303913891427,\"OutPoint_Y\":297.21030751597522},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -258.8309537310077, 14.790477788784642]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.98660023945785613, -0, -0, 1.0127745463822073, 7.5897658853896965, -3.7483052243093384]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":307.98765662154068,\"AnchorPoint_Y\":526.40806463936553,\"inPoint_Y\":555.58846167930153,\"OutPoint_X\":307.98765662154068,\"AnchorPoint_X\":307.98765662154068,\"OutPoint_Y\":497.22766759942954},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":9223372036854775807}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":580,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":680.00002878904479},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":660,\"inPoint_Y\":620,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":700},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 20, 20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 640, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 39, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":220,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 60, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{540, 0}\",\"VNPrevPointForRadiusKey\":\"{540, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":300,\"AnchorPoint_X\":280,\"OutPoint_Y\":600},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[],"methodSignature":"reversePathDirection","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":600,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":240,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -60, 100]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -8.3360061810856791, -0.18596575427056905]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12624915594960839, -0.84513498021431133]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0820554726071352, -0, -0, 1, -28.572516179113212, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 3.7693360211719664, 0.58007166688469169]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.8911271920692454, -0, 58.840421083655528]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0.018101184397323777, -6.1834757589472247]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.5616683932951787, -14.721591768371582]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.4335340527209723, 13.544422687729025]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":43}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.10440918421369,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":598.11903819000008},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":366.0442444460744,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.21462016330969},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":337.32713106884489,\"AnchorPoint_Y\":505.14299981511687,\"inPoint_Y\":505.14299981511687,\"OutPoint_X\":337.32713106884489,\"AnchorPoint_X\":337.32713106884489,\"OutPoint_Y\":505.14299981511687},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":282.38947067174871,\"AnchorPoint_Y\":502.68576551434728,\"inPoint_Y\":502.68576551434728,\"OutPoint_X\":282.38947067174871,\"AnchorPoint_X\":282.38947067174871,\"OutPoint_Y\":502.68576551434728},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"VNPrevPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":303.99512941310888,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":487.18557523329599,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":45}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 618.5121544161118, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -17.095091220096265, 18.636572344325828]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 2.4740749822735211, -26.734403557988003]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[46]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":50}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":50}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":323.12940602919167,\"AnchorPoint_Y\":534.54856008655031,\"inPoint_Y\":534.54856008655031,\"OutPoint_X\":323.12940602919167,\"AnchorPoint_X\":323.12940602919167,\"OutPoint_Y\":534.54856008655031},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[47,48]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":46},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13822251558303833,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -2.1531569701203352, 25.991399082341331]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":308.00218177519929,\"AnchorPoint_Y\":532.86800447564667,\"inPoint_Y\":504.53379546064855,\"OutPoint_X\":307.49158198784681,\"AnchorPoint_X\":307.49158198784681,\"OutPoint_Y\":532.86800447564667},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.54481307650724,\"AnchorPoint_Y\":482.22702540901258,\"inPoint_Y\":482.22702540901258,\"OutPoint_X\":307.54481307650724,\"AnchorPoint_X\":307.54481307650724,\"OutPoint_Y\":482.22702540901258},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"VNPrevPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":344.58533198784681,\"AnchorPoint_Y\":482.18050447564667,\"inPoint_Y\":482.18050447564667,\"OutPoint_X\":323.06817538141632,\"AnchorPoint_X\":344.58533198784681,\"OutPoint_Y\":488.07641427863712},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.34003453224391933]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.1954377384541317]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 692.07691397569363, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 57.024957634971202, 19.792602829855639]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":307.04977456552314,\"AnchorPoint_Y\":532.53992937509292,\"inPoint_Y\":504.20572036009486,\"OutPoint_X\":307.56037435287561,\"AnchorPoint_X\":307.56037435287561,\"OutPoint_Y\":532.53992937509292},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"VNPrevPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.50714326421519,\"AnchorPoint_Y\":481.89895030845889,\"inPoint_Y\":481.89895030845889,\"OutPoint_X\":307.50714326421519,\"AnchorPoint_X\":307.50714326421519,\"OutPoint_Y\":481.89895030845889},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"VNPrevPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":270.46662435287561,\"AnchorPoint_Y\":481.85242937509298,\"inPoint_Y\":481.85242937509298,\"OutPoint_X\":291.68464211968808,\"AnchorPoint_X\":270.46662435287561,\"OutPoint_Y\":491.96348695323337},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"VNPrevPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":55},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0330248439984473, -0, -0, 0.99999999999999989, -11.379876833051835, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[55]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":56},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":55}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -290.115478515625, 62.302734375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.89467432944640213,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.90097236067321962,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.031593452065677957,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.91739226661025597,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.019517801575741518,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93616537665754818,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0091232041181144082,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94328004740819738,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0018620895127118644,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95006183205443251,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95531556443573817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9587083584477003,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96669617604784863,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96977202191904677,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97283138648692502,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97731792325435829,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9818318081185089,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9863455118694362,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99094089768406901,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99553628349870182,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -138.240478515625, -358.288818359375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10.2197265625, 7.887451171875]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.73681640625, -1.3956298828125]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96954748896640086,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96362693977790437,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.95841712343394081,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.954131593465262,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.93934857275056949,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.83357840083997725,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.80253639308086555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.76741239856207288,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.7565612097807517,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.73508462058656032,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.69529915290432798,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.68285743522209563,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.66214385499715267,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.64970324957289294,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.63305608449601369,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.62061547907175396,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.60169597095671978,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.59555519468963558,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5870419721668565,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.57588046341116172,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.56733832218109337,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5457271497722096,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.51220146996013671,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.45939702270785876,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.41278562784738043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.38011749893223234,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.35006206399487472,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.33336373505125283,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31040673049544421,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29577720138097952,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.27499243664578588,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.25639993237471526,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24782776017938496,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23669294561503418,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22722206897779043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22344372864464693,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21991675861332574,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21874110193621868,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2197966347522779,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23113499252562641,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24180488325740318,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2529452591116173,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.28146132901480636,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29042946504840544,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29303437322038722,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30319262528473806,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31050016016514809,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31284924900341687,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31402379342255127,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31192273811218679,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31087054207004555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30975717183940776,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.3074892778331435,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0000066161593433, -0, -0, 1.0000066161593433, -0.00084115369224984499, -0.0015278888873493538]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 106.25150911801597, 215.10157288799655]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1024, 1024}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1018.1834573966, 1022.316502396953}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1008.5644489681886, 1015.9218488910001}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1003.3383988868811, 1012.188740268123}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {959.56182404754463, 968.37466824766148}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {930.99873639690077, 938.04865943448704}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {883.88375480634159, 898.86742705675647}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {851.39277451811745, 876.13749149913815}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {807.42735481491377, 847.80421878677203}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {780.8131139470363, 833.66058405713125}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {758.18676120653936, 823.6793880913026}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745.42543866806363, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.45307691385142, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.65313908147255}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {735.7456294412641, 820.1359296408807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {734.20024226104533, 819.105537302637}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {730.82720724054207, 815.73250228213374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {722.51078545309133, 807.41391622785272}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {715.22999050970566, 800.13291995732015}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {710.02528110597814, 794.3195482564854}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {708.99086222479468, 792.76802059828378}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {705.03881032957679, 786.83843280185465}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {703.71729893680003, 785.04591654871797}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {702.86608775936838, 784.19455437592615}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {701.16326275021129, 782.49188036212945}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {700.77188277648224, 782.10024872946656}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.97704320020512, 781.30550981676288}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.12865060283116, 780.87985389626033}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {698.70304501411556, 780.45414764397106}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {695.54100084364723, 776.8204946317021}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {693.70127337454369, 774.98056583545167}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.86235121402819, 772.67134345959084}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.97672115541332, 769.84279770809485}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.03491276184081, 768.90119064166925}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {686.67817717929256, 766.07264489017348}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {683.90469440249581, 763.76603976722345}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {682.53607245732564, 762.86897633206718}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {681.17026909221318, 762.39756881741346}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {678.95083862390538, 761.54620664462163}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.30580256688177, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.07408308165805, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.28025820206608, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {671.48603066818009, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.54341696601955, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.17600298373122, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.39103649860635, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.44842279644581, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.02281720772999, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.226367014277, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {663.57830298909857, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.7291050831368, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.92701772956843, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.54691207607016, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.69429082217562}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.30406847954168}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.91817467056762}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.52795232793369}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.12605301077474}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.73814593033103}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.96107347477482}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.97694686201953, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 757.18274272455005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 756.35493582795493}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.94518475206382}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.5517915068649}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.78614436689986}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.3960730196261}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.00237778370661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.60918586565458}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.21065877820774}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.81701387407497}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.41430924832821}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 749.69714774089903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 748.93285955917622}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.99004452986878}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.13868235707696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.41906127678772, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.65522608114543, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.27149653900142, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.88132452815421, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.48793128295529, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.69429966956, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.30453031300658, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.68503862079888, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.0695734715307, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.47263041978476, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.86843959074758, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.25498771294906, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.63670398362319, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.02566803158879, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.83158060094979, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {664.08383545516563, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.7862578100287, 746.7378903392264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.06307457617595, 747.16359659151567}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.70851328749336, 748.01490843252077}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.98533005364084, 748.44061468481004}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.66399580516031, 748.84437627807847}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.94081257130756, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.19387273411121, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.58525270784025, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.20474440004818, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.93235683113426, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.1381292972485, 747.52296554887494}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.87500277709569, 747.09725929658589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.47194582884163, 746.2471050866759}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.64368594616599, 745.41864387685314}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 745.02308636482417}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":330.67709661876142,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":247.83495910094825},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":214.25650324349817,\"inPoint_Y\":214.25650324349817,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":214.25650324349817},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":247.83495910094825,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":330.67709661876142},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":364.25550551101242,\"inPoint_Y\":364.25550551101242,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":364.25550551101242},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":580.67544258986891,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":497.83330507205574},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":464.25484921460566,\"inPoint_Y\":464.25484921460566,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":464.25484921460566},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":497.83330507205574,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":580.67544258986891},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":614.2538514821199,\"inPoint_Y\":614.2538514821199,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":614.2538514821199},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":97.57351080287799,\"AnchorPoint_Y\":309.16721333385931,\"inPoint_Y\":309.16721333385931,\"OutPoint_X\":97.57351080287799,\"AnchorPoint_X\":97.57351080287799,\"OutPoint_Y\":309.16721333385931},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":221.36797043606543,\"AnchorPoint_Y\":443.90288627635027,\"inPoint_Y\":443.90288627635027,\"OutPoint_X\":221.36797043606543,\"AnchorPoint_X\":221.36797043606543,\"OutPoint_Y\":443.90288627635027},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":361.08361414401247,\"AnchorPoint_Y\":309.6035637643763,\"inPoint_Y\":309.6035637643763,\"OutPoint_X\":361.08361414401247,\"AnchorPoint_X\":361.08361414401247,\"OutPoint_Y\":309.6035637643763},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":377.82365759764593,\"AnchorPoint_Y\":307.07623767819223,\"inPoint_Y\":307.07623767819223,\"OutPoint_X\":377.82365759764593,\"AnchorPoint_X\":377.82365759764593,\"OutPoint_Y\":307.07623767819223},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":503.2994629767461,\"AnchorPoint_Y\":440.11243356580462,\"inPoint_Y\":440.11243356580462,\"OutPoint_X\":503.2994629767461,\"AnchorPoint_X\":503.2994629767461,\"OutPoint_Y\":440.11243356580462},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":644.91269001662772,\"AnchorPoint_Y\":307.50708423933065,\"inPoint_Y\":307.50708423933065,\"OutPoint_X\":644.91269001662772,\"AnchorPoint_X\":644.91269001662772,\"OutPoint_Y\":307.50708423933065},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":516.45695350382334,\"AnchorPoint_Y\":154.80813254227905,\"inPoint_Y\":154.80813254227905,\"OutPoint_X\":516.45695350382334,\"AnchorPoint_X\":516.45695350382334,\"OutPoint_Y\":154.80813254227905},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":223.97172601151692,\"AnchorPoint_Y\":448.88181029919406,\"inPoint_Y\":448.88181029919406,\"OutPoint_X\":223.97172601151692,\"AnchorPoint_X\":223.97172601151692,\"OutPoint_Y\":448.88181029919406},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":78.252109034647219,\"AnchorPoint_Y\":300.1041732651538,\"inPoint_Y\":300.1041732651538,\"OutPoint_X\":78.252109034647219,\"AnchorPoint_X\":78.252109034647219,\"OutPoint_Y\":300.1041732651538},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":648.07861404640448,\"AnchorPoint_Y\":302.9355554780409,\"inPoint_Y\":302.9355554780409,\"OutPoint_X\":648.07861404640448,\"AnchorPoint_X\":648.07861404640448,\"OutPoint_Y\":302.9355554780409},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":506.17105679039673,\"AnchorPoint_Y\":444.7847840681311,\"inPoint_Y\":444.7847840681311,\"OutPoint_X\":506.17105679039673,\"AnchorPoint_X\":506.17105679039673,\"OutPoint_Y\":444.7847840681311},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":217.51314851045686,\"AnchorPoint_Y\":156.11368443580113,\"inPoint_Y\":156.11368443580113,\"OutPoint_X\":217.51314851045686,\"AnchorPoint_X\":217.51314851045686,\"OutPoint_Y\":156.11368443580113},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":362.67570746371234,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.18143434711533,\"AnchorPoint_X\":362.18143434711533,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.23296334642896,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.23296334642896,\"AnchorPoint_X\":362.23296334642896,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":398.08909406988482,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":377.54953250055337,\"AnchorPoint_X\":398.08909406988482,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":361.7537539269963,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.24802704359331,\"AnchorPoint_X\":362.24802704359331,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.19649804427968,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.19649804427968,\"AnchorPoint_X\":362.19649804427968,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":326.34036732082382,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":346.87992889015482,\"AnchorPoint_X\":326.34036732082382,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":\"Mac App icon\"}","argumentGID":0}],"methodSignature":"title","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {740, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}]],"isGroupingUndo":false,"currentUndoGroup":[],"currentRedoGroup":[],"isGroupingRedo":false,"redoStack":[],"nextGID":57}
\ No newline at end of file
diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml
new file mode 100644
index 0000000..3bade7b
--- /dev/null
+++ b/docs/mkdocs/mkdocs.yml
@@ -0,0 +1,93 @@
+site_name: Bumble
+
+use_directory_urls: false
+
+nav:
+  - Introduction: index.md
+  - Getting Started: getting_started.md
+  - Development:
+    - Python Environments: development/python_environments.md
+  - Use Cases:
+    - Overview: use_cases/index.md
+    - Use Case 1: use_cases/use_case_1.md
+    - Use Case 2: use_cases/use_case_2.md
+    - Use Case 3: use_cases/use_case_3.md
+    - Use Case 4: use_cases/use_case_4.md
+    - Use Case 5: use_cases/use_case_5.md
+    - Use Case 6: use_cases/use_case_6.md
+  - Components:
+    - Controller: components/controller.md
+    - Host: components/host.md
+    - GATT: components/gatt.md
+    - Security Manager: components/security_manager.md
+  - Transports:
+    - Overview: transports/index.md
+    - Serial: transports/serial.md
+    - USB: transports/usb.md
+    - PTY: transports/pty.md
+    - UDP: transports/udp.md
+    - TCP Client: transports/tcp_client.md
+    - TCP Server: transports/tcp_server.md
+    - WebSocket Client: transports/ws_client.md
+    - WebSocket Server: transports/ws_server.md
+    - VHCI: transports/vhci.md
+    - HCI Socket: transports/hci_socket.md
+    - Android Emulator: transports/android_emulator.md
+    - File: transports/file.md
+  - API:
+    - Guide: api/guide.md
+    - Examples: api/examples.md
+    - Reference: api/reference.md
+  - Apps & Tools:
+    - Overview: apps_and_tools/index.md
+    - Console: apps_and_tools/console.md
+    - Link Relay: apps_and_tools/link_relay.md
+    - HCI Bridge: apps_and_tools/hci_bridge.md
+    - Golden Gate Bridge: apps_and_tools/gg_bridge.md
+    - Show: apps_and_tools/show.md
+  - Hardware:
+    - Overview: hardware/index.md
+  - Platforms:
+    - Overview: platforms/index.md
+    - macOS: platforms/macos.md
+    - Linux: platforms/linux.md
+    - Windows: platforms/windows.md
+    - Android: platforms/android.md
+  - Examples:
+    - Overview: examples/index.md
+
+copyright: Copyright 2021-2022 Google LLC
+
+theme:
+  name: 'material'
+  logo: 'images/logo.png'
+  favicon: 'images/favicon.ico'
+  custom_dir: 'theme'
+
+plugins:
+  - mkdocstrings:
+      handlers:
+        python:
+          paths: [../..]
+
+docs_dir: 'src'
+
+edit_uri: ''
+
+# Repo info
+repo_name: 'GitHub'
+repo_url: https://github.com/google/bumble
+
+# Extensions
+markdown_extensions:
+  - admonition
+  - attr_list
+  - pymdownx.details
+  - pymdownx.superfences
+  - pymdownx.emoji:
+      emoji_index: !!python/name:materialx.emoji.twemoji
+      emoji_generator: !!python/name:materialx.emoji.to_svg
+  - codehilite:
+      guess_lang: false
+  - toc:
+      permalink: true
diff --git a/docs/mkdocs/requirements.txt b/docs/mkdocs/requirements.txt
new file mode 100644
index 0000000..a9b8452
--- /dev/null
+++ b/docs/mkdocs/requirements.txt
@@ -0,0 +1,6 @@
+# This requirements file is for python3
+mkdocs == 1.2.3
+mkdocs-material == 7.1.7
+mkdocs-material-extensions == 1.0.1
+pymdown-extensions == 8.2
+mkdocstrings == 0.15.1
\ No newline at end of file
diff --git a/docs/mkdocs/src/api/examples.md b/docs/mkdocs/src/api/examples.md
new file mode 100644
index 0000000..bcf30a3
--- /dev/null
+++ b/docs/mkdocs/src/api/examples.md
@@ -0,0 +1,2 @@
+API EXAMPLES
+============
\ No newline at end of file
diff --git a/docs/mkdocs/src/api/guide.md b/docs/mkdocs/src/api/guide.md
new file mode 100644
index 0000000..b799faf
--- /dev/null
+++ b/docs/mkdocs/src/api/guide.md
@@ -0,0 +1,2 @@
+API DEVELOPER GUIDE
+===================
\ No newline at end of file
diff --git a/docs/mkdocs/src/api/reference.md b/docs/mkdocs/src/api/reference.md
new file mode 100644
index 0000000..cedc2c1
--- /dev/null
+++ b/docs/mkdocs/src/api/reference.md
@@ -0,0 +1,19 @@
+Bumble Python API
+=================
+
+# Classes
+
+## Address
+::: bumble.hci.Address
+
+## HCI_Packet
+::: bumble.hci.HCI_Packet
+
+## HCI Commands
+
+### HCI_Command
+::: bumble.hci.HCI_Command
+
+### HCI_Disconnect_Command
+::: bumble.hci.HCI_Disconnect_Command
+
diff --git a/docs/mkdocs/src/apps_and_tools/console.md b/docs/mkdocs/src/apps_and_tools/console.md
new file mode 100644
index 0000000..9abdf2b
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/console.md
@@ -0,0 +1,31 @@
+CONSOLE APP
+===========
+
+![logo](../images/console_screenshot.png){ width=300 height=300 }
+
+The Console app is an interactive text user interface that offers a number of functions, including:
+
+  * scanning
+  * advertising
+  * connecting to devices
+  * changing connection parameters
+  * discovering GATT services and characteristics
+  * read & write GATT characteristics
+
+The console user interface has 3 main panes:
+
+  * a display pane, that displays information, depending on a user-selected mode. The `show` command can be used to switch what is displayed in this pane
+  * a command history pane that shows a short history of the last commands and their results
+  * a command pane, with tab completion, where you can enter commands
+
+In addition to the display panes, the console has a status bar, showing the scanning state and the connection state.
+
+!!! info "Running the console app"
+    ```
+    python console.py <transport-spec>
+    ```
+
+    Example:
+    ```
+    python console.py usb:0
+    ```
diff --git a/docs/mkdocs/src/apps_and_tools/gatt_dump.md b/docs/mkdocs/src/apps_and_tools/gatt_dump.md
new file mode 100644
index 0000000..c9d13b6
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/gatt_dump.md
@@ -0,0 +1,2 @@
+GATT DUMP TOOL
+==============
diff --git a/docs/mkdocs/src/apps_and_tools/gg_bridge.md b/docs/mkdocs/src/apps_and_tools/gg_bridge.md
new file mode 100644
index 0000000..950f47c
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/gg_bridge.md
@@ -0,0 +1,2 @@
+GOLDEN GATE BRIDGE
+==================
\ No newline at end of file
diff --git a/docs/mkdocs/src/apps_and_tools/hci_bridge.md b/docs/mkdocs/src/apps_and_tools/hci_bridge.md
new file mode 100644
index 0000000..c8def63
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/hci_bridge.md
@@ -0,0 +1,32 @@
+HCI BRIDGE
+==========
+
+This tool acts as a simple bridge between two HCI transports, with a host on one side and
+a controller on the other. All the HCI packets bridged between the two are printed on the console
+for logging. This bridge also has the ability to short-circuit some HCI packets (respond to them
+with a fixed response instead of bridging them to the other side), which may be useful when used with
+a host that send custom HCI commands that the controller may not understand.
+
+
+!!! info "Running the HCI bridge tool"
+    ```
+    python hci_bridge.py <host-transport-spec> <controller-transport-spec> [command-short-circuit-list]
+    ```
+
+!!! example "UDP to Serial"
+    ```
+    python hci_bridge.py udp:0.0.0.0:9000,127.0.0.1:9001 serial:/dev/tty.usbmodem0006839912171,1000000 0x3f:0x0070,0x3f:0x0074,0x3f:0x0077,0x3f:0x0078
+    ```
+
+!!! example "PTY to Link Relay"
+    ```
+    python hci_bridge.py serial:emulated_uart_pty,1000000 link-relay:ws://127.0.0.1:10723/test
+    ```
+
+    In this example, an emulator that exposes a PTY as an interface to its HCI UART is running as
+    a Bluetooth host, and we are connecting it to a virtual controller attached to a link relay
+    (through which the communication with other virtual controllers will be mediated).
+
+    NOTE: this assumes you're running a Link Relay on port `10723`.
+
+
diff --git a/docs/mkdocs/src/apps_and_tools/index.md b/docs/mkdocs/src/apps_and_tools/index.md
new file mode 100644
index 0000000..cd8b346
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/index.md
@@ -0,0 +1,14 @@
+APPS & TOOLS
+============
+
+Included in the project are a few apps and tools, built on top of the core libraries.
+These include:
+
+  * [Console](console.md) - an interactive text-based console
+  * [Pair](pair.md) - Pair/bond two devices (LE and Classic)
+  * [Unbond](unbond.md) - Remove a previously established bond
+  * [HCI Bridge](hci_bridge.md) - a HCI transport bridge to connect two HCI transports and filter/snoop the HCI packets
+  * [Golden Gate Bridge](gg_bridge.md) - a bridge between GATT and UDP to use with the Golden Gate "stack tool"
+  * [Show](show.md) - Parse a file with HCI packets and print the details of each packet in a human readable form
+  * [Link Relay](link_relay.md) - WebSocket relay for virtual RemoteLink instances to communicate with each other.
+
diff --git a/docs/mkdocs/src/apps_and_tools/link_relay.md b/docs/mkdocs/src/apps_and_tools/link_relay.md
new file mode 100644
index 0000000..533c525
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/link_relay.md
@@ -0,0 +1,34 @@
+LINK RELAY TOOL
+===============
+
+The Link Relay is a WebSocket relay, which acts like an online chat system, where each "chat room" can be joined by multiple virtual controllers, which can then communicate with each other, as if connected with radio communication.
+
+```
+usage: python link_relay.py [-h] [--log-level LOG_LEVEL] [--log-config LOG_CONFIG] [--port PORT]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --log-level LOG_LEVEL
+                        logger level
+  --log-config LOG_CONFIG
+                        logger config file (YAML)
+  --port PORT           Port to listen on
+```
+
+(the default port is `10723`)
+
+When running, the link relay waits for connections on its listening port.
+The WebSocket path used by a connecting client indicates which virtual "chat room" to join.
+
+
+!!! tip "Connecting to the relay as a controller"
+    Most of the examples and tools that take a transport moniker as an argument also accept a link relay moniker, which is equivalent to a transport to a virtual controller that is connected to a relay.
+    The moniker syntax is: `link-relay:ws://<hostname>/<room>` where `<hostname>` is the hostname to connect to and `<room>` is the virtual "chat room" in a relay.
+
+    Example: `link-relay:ws://localhost:10723/test` will join the `test` "chat room"
+
+!!! tip "Connecting to the relay as an observer"
+    It is possible to connect to a "chat room" in a relay as an observer, rather than a virtual controller. In this case, a text-based console can be used to observe what is going on in the "chat room". Tools like [`wscat`](https://github.com/websockets/wscat#readme) or [`websocat`](https://github.com/vi/websocat) can be used for that.
+
+    Example: `wscat --connect ws://localhost:10723/test`
+
diff --git a/docs/mkdocs/src/apps_and_tools/pair.md b/docs/mkdocs/src/apps_and_tools/pair.md
new file mode 100644
index 0000000..7a5ba30
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/pair.md
@@ -0,0 +1,2 @@
+PAIR TOOL
+=========
diff --git a/docs/mkdocs/src/apps_and_tools/show.md b/docs/mkdocs/src/apps_and_tools/show.md
new file mode 100644
index 0000000..a847326
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/show.md
@@ -0,0 +1,2 @@
+SHOW TOOL
+=========
diff --git a/docs/mkdocs/src/apps_and_tools/unbond.md b/docs/mkdocs/src/apps_and_tools/unbond.md
new file mode 100644
index 0000000..60edd15
--- /dev/null
+++ b/docs/mkdocs/src/apps_and_tools/unbond.md
@@ -0,0 +1,2 @@
+UNBOND TOOL
+===========
diff --git a/docs/mkdocs/src/components/controller.md b/docs/mkdocs/src/components/controller.md
new file mode 100644
index 0000000..1537712
--- /dev/null
+++ b/docs/mkdocs/src/components/controller.md
@@ -0,0 +1,2 @@
+CONTROLLER
+==========
\ No newline at end of file
diff --git a/docs/mkdocs/src/components/gatt.md b/docs/mkdocs/src/components/gatt.md
new file mode 100644
index 0000000..614f03c
--- /dev/null
+++ b/docs/mkdocs/src/components/gatt.md
@@ -0,0 +1,2 @@
+GATT
+====
\ No newline at end of file
diff --git a/docs/mkdocs/src/components/host.md b/docs/mkdocs/src/components/host.md
new file mode 100644
index 0000000..5a34473
--- /dev/null
+++ b/docs/mkdocs/src/components/host.md
@@ -0,0 +1,2 @@
+HOST
+====
\ No newline at end of file
diff --git a/docs/mkdocs/src/components/security_manager.md b/docs/mkdocs/src/components/security_manager.md
new file mode 100644
index 0000000..4ce3d74
--- /dev/null
+++ b/docs/mkdocs/src/components/security_manager.md
@@ -0,0 +1,2 @@
+SECURITY MANAGER
+================
\ No newline at end of file
diff --git a/docs/mkdocs/src/development/python_environments.md b/docs/mkdocs/src/development/python_environments.md
new file mode 100644
index 0000000..0be529e
--- /dev/null
+++ b/docs/mkdocs/src/development/python_environments.md
@@ -0,0 +1,45 @@
+PYTHON ENVIRONMENTS
+===================
+
+When you don't want to install Bumble in your main/default python environment, 
+using a virtual environment, where the package and its dependencies can be
+installed, isolated from the rest, may be useful.
+
+There are many flavors of python environments and dependency managers. 
+This page describes a few of the most common ones.
+
+
+## venv
+
+`venv` is a standard module that is included with python.
+Visit the [`venv` documentation](https://docs.python.org/3/library/venv.html) page for details.
+
+## Pyenv
+
+`pyenv` lets you easily switch between multiple versions of Python. It's simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well.  
+Visit the [`pyenv` site](https://github.com/pyenv/pyenv) for instructions on how to install
+and use `pyenv`
+
+## Conda
+
+Conda is a convenient package manager and virtual environment.
+The file `environment.yml` is a Conda environment file that you can use to create
+a new Conda environment. Once created, you can simply activate this environment when
+working with Bumble.  
+Visit the [Conda site](https://docs.conda.io/en/latest/) for instructions on how to install
+and use Conda.
+A few useful commands:  
+
+### Create a new `bumble` Conda environment
+```
+$ conda env create -f environment.yml
+```
+This will create a new environment, named `bumble`, which you can then activate with:
+```
+$ conda activate bumble
+```
+
+### Update an existing `bumble` environment
+```
+$ conda env update -f environment.yml
+```
diff --git a/docs/mkdocs/src/examples/index.md b/docs/mkdocs/src/examples/index.md
new file mode 100644
index 0000000..9e6285f
--- /dev/null
+++ b/docs/mkdocs/src/examples/index.md
@@ -0,0 +1,72 @@
+EXAMPLES
+========
+
+The project includes a few simple example applications the illustrate some of the ways the library APIs can be used.
+These examples include:
+
+## `battery_service.py`
+Run a simple device example with a GATT server that exposes a standard Battery Service.
+
+## `get_peer_device_info.py`
+An app that connects to a device, discovers its GATT services, and, if the Device Information Service is found, looks for a Manufacturer Name characteristics, reads and prints it.
+
+## `keyboard.py`
+An app that implements a virtual keyboard or mouse, or can connect to a real keyboard and receive key presses.
+
+## `run_a2dp_info.py`
+An app that connects to a device, phone or computer and inspects its A2DP (Advanced Audio Profile) capabilities
+
+## `run_a2dp_source.py`
+An app that can connect to a Bluetooth speaker and play audio.
+
+## `run_a2dp_sink.py`
+An app that implements a virtual Bluetooth speaker that can receive audio.
+
+## `run_advertiser.py`
+An app that runs a simple device that just advertises (BLE).
+
+## `run_classic_connect.py`
+An app that connects to a Bluetooth Classic device and prints its services.
+
+## `run_classic_discoverable.py`
+An app that implements a discoverable and connectable Bluetooth Classic device.
+
+## `run_classic_discovery.py`
+An app that discovers Bluetooth Classic devices and prints their information.
+
+## `run_connect_and_encrypt.py`
+An app that connected to a device (BLE) and encrypts the connection.
+
+## `run_controller_with_scanner.py`
+
+## `run_controller.py`
+Creates two linked controllers, attaches one to a transport, and the other to a local host with a GATT server application. This can be used, for example, to attach a virtual controller to a native stack, like BlueZ on Linux, and use the native tools, like `bluetoothctl`, to scan and connect to the GATT server included in the example.
+
+## `run_gatt_client_and_server.py`
+Runs a local GATT server and GATT client, connected to each other. The GATT client discovers and logs all the services and characteristics exposed by the GATT server
+
+## `run_gatt_client.py`
+A simple GATT client that either connects to another BLE device or waits for a connection, then dumps its GATT database.
+
+## `run_gatt_server.py`
+A simple GATT server that either connects to another BLE device or waits for connections.
+
+## `run_hfp_gateway.py`
+A app that implements a Hands Free gateway. It can connect to a Hands Free headset.
+
+## `run_hfp_handsfree.py`
+A app that implements a Hands Free headset. It can simulate some of the events that a real headset would
+emit, like picking up or hanging up a call, pressing a button, etc.
+
+## `run_notifier.py`
+An app that implements a GATT server with characteristics that can be subscribed to, and emits notifications
+for those characteristics at regular intervals.
+
+## `run_rfcomm_client.py`
+An app that connects to an RFComm server and bridges the RFComm channel to a local TCP socket
+
+## `run_rfcomm_server.py`
+An app that implements an RFComm server and, when a connection is received, bridges the channel to a local TCP socket
+
+## `run_scanner.py`
+An app that scan for BLE devices and print the advertisements received.
\ No newline at end of file
diff --git a/docs/mkdocs/src/getting_started.md b/docs/mkdocs/src/getting_started.md
new file mode 100644
index 0000000..832691a
--- /dev/null
+++ b/docs/mkdocs/src/getting_started.md
@@ -0,0 +1,106 @@
+GETTING STARTED WITH BUMBLE
+===========================
+
+# Prerequisites
+
+You need Python 3.8 or above. Python >= 3.9 is recommended, but 3.8 should be sufficient if
+necessary (there may be some optional functionality that will not work on some platforms with
+python 3.8).  
+Visit the [Python site](https://www.python.org/) for instructions on how to install Python
+for your platform.  
+Throughout the documentation, when shell commands are shown, it is assumed that you can
+invoke Python as
+```
+$ python
+```
+If invoking python is different on your platform (it may be `python3` for example, or just `py` or `py.exe`), 
+adjust accordingly.
+
+You may be simply using Bumble as a module for your own application or as a dependency to your own 
+module, or you may be working on modifying or contributing to the Bumble module or example code
+itself.
+
+# Using Bumble As A Python Module
+
+## Installing
+
+You may choose to install the Bumble module from an online package repository, with a package
+manager, or from source.
+
+!!! tip "Python Virtual Environments"
+    When you install Bumble, you have the option to install it as part of your default
+    python environment, or in a virtual environment, such as a `venv`, `pyenv` or `conda` environment.
+    See the [Python Environments page](development/python_environments.md) page for details.
+
+### Install From Source
+
+Install with `pip`. Run in a command shell in the directory where you downloaded the source
+distribution
+```
+$ python -m pip install -e .
+```
+
+### Install from GitHub
+
+You can install directly from GitHub without first downloading the repo.
+
+Install the latest commit from the main branch with `pip`:
+```
+$ python -m pip install git+https://github.com/google/bumble.git
+```
+
+You can specify a specific tag.
+
+Install tag `v0.0.1` with `pip`:
+```
+$ python -m pip install git+https://github.com/google/bumble.git@v0.0.1
+```
+
+You can also specify a specific commit.
+
+Install commit `27c0551` with `pip`:
+```
+$ python -m pip install git+https://github.com/google/bumble.git@27c0551
+```
+
+# Working On The Bumble Code
+When you work on the Bumble code itself, and run some of the tests or example apps, or import the
+module in your own code, you typically either install the package from source in "development mode" as described above, or you may choose to skip the install phase. 
+
+## Without Installing
+If you prefer not to install the package (even in development mode), you can load the module directly from its location in the project. 
+A simple way to do that is to set your `PYTHONPATH` to
+point to the root project directory, where the `bumble` subdirectory is located. You may set
+`PYTHONPATH` globally, or locally with each command line execution (on Unix-like systems).
+
+Example with a global `PYTHONPATH`, from a unix shell, when the working directory is the root 
+directory of the project.
+
+```bash
+$ export PYTHONPATH=.
+$ python apps/console.py serial:/dev/tty.usbmodem0006839912171
+```
+
+or running an example, with the working directory set to the `examples` subdirectory
+```bash
+$ cd examples
+$ export PYTHONPATH=..
+$ python run_scanner.py usb:0
+```
+
+Or course, `export PYTHONPATH` only needs to be invoked once, not before each app/script execution.
+
+Setting `PYTHONPATH` locally with each command would look something like:
+```
+$ PYTHONPATH=. python examples/run_advertiser.py examples/device1.json serial:/dev/tty.usbmodem0006839912171
+```
+
+# Where To Go Next
+Once you've installed or downloaded Bumble, you can either start using some of the 
+[Bundled apps and tools](apps_and_tools/index.md), or look at the [examples](examples/index.md)
+to get a feel for how to use the APIs, and start writing your own applications.
+
+Depending on the use case you're interested in exploring, you may need to use a physical Bluetooth
+controller, like a USB dongle or a board with a Bluetooth radio. Visit the [Hardware page](hardware/index.md)
+for more information on using a physical radio, and/or the [Transports page](transports/index.md) for more 
+details on interfacing with either hardware modules or virtual controllers over various transports.
diff --git a/docs/mkdocs/src/hardware/index.md b/docs/mkdocs/src/hardware/index.md
new file mode 100644
index 0000000..e088fe1
--- /dev/null
+++ b/docs/mkdocs/src/hardware/index.md
@@ -0,0 +1,19 @@
+HARDWARE
+========
+
+The Bumble Host connects to a controller over an [HCI Transport](../transports/index.md). 
+To use a hardware controller attached to the host on which the host application is running, the transport is typically either [HCI over UART](../transports/serial.md) or [HCI over USB](../transports/usb.md). 
+On Linux, the [VHCI Transport](../transports/vhci.md) can be used to communicate with any controller hardware managed by the operating system. Alternatively, a remote controller (a phyiscal controller attached to a remote host) can be used by connecting one of the networked transports (such as the [TCP Client transport](../transports/tcp_client.md), the [TCP Server transport](../transports/tcp_server.md) or the [UDP Transport](../transports/udp.md)) to an [HCI Bridge](../apps_and_tools/hci_bridge) bridging the network transport to a physical controller on a remote host. 
+
+In theory, any controller that is compliant with the HCI over UART or HCI over USB protocols can be used. 
+
+HCI over USB is very common, implemented by a number of commercial Bluetooth dongles. 
+
+It is also possible to use an embedded development board, running a specialized application, such as the [`HCI UART`](https://docs.zephyrproject.org/latest/samples/bluetooth/hci_uart/README.html) and [`HCI USB`](https://docs.zephyrproject.org/latest/samples/bluetooth/hci_usb/README.html) demo applications from the [Zephyr project](https://www.zephyrproject.org/), or the [`blehci`](https://mynewt.apache.org/latest/tutorials/ble/blehci_project.html) application from [mynewt/nimble](https://mynewt.apache.org/)
+
+Some specific USB dongles and embedded boards that are known to work include:
+  
+  * [Nordic nRF52840 DK board](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk) with the Zephyr `HCI UART` application
+  * [Nordic nRF52840 DK board](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk) with the mynewt `blehci` application
+  * [Nordic nrf52840 dongle](https://www.nordicsemi.com/Products/Development-hardware/nRF52840-Dongle) with the Zephyr `HCI USB` application
+  * [BT820 USB Dongle](https://www.lairdconnect.com/wireless-modules/bluetooth-modules/bluetooth-42-and-40-modules/bt800-series-bluetooth-module)
diff --git a/docs/mkdocs/src/images/bumble_layers.svg b/docs/mkdocs/src/images/bumble_layers.svg
new file mode 100644
index 0000000..3da9f58
--- /dev/null
+++ b/docs/mkdocs/src/images/bumble_layers.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 539.0209973753281 600.7742782152231" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l539.021 0l0 600.7743l-539.021 0l0 -600.7743z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l539.021 0l0 600.7743l-539.021 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m362.34805 205.27522l0 -159.87402" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m362.34805 205.27522l0 -159.87402" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m94.91498 205.27522l0 -159.87402" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m94.91498 205.27522l0 -159.87402" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m131.88086 534.9836l0 -116.81891" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m131.88086 534.9836l0 -116.81891" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m404.9701 534.9836l0 -124.62991" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m404.9701 534.9836l0 -124.62991" fill-rule="evenodd"/><g filter="url(#shadowFilter-p.1)"><use xlink:href="#p.1" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.1" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.1"><path fill="#a4c2f4" d="m4.8992295 210.33295l0 0c0 -8.175705 6.627728 -14.803436 14.803446 -14.803436l497.4482 0c3.9261475 0 7.6914673 1.5596466 10.467651 4.3358307c2.776184 2.776184 4.3358154 6.5414886 4.3358154 10.467606l0 59.212006c0 8.17572 -6.6277466 14.803467 -14.803467 14.803467l-497.4482 0c-8.175717 0 -14.803446 -6.6277466 -14.803446 -14.803467z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 210.33295l0 0c0 -8.175705 6.627728 -14.803436 14.803446 -14.803436l497.4482 0c3.9261475 0 7.6914673 1.5596466 10.467651 4.3358307c2.776184 2.776184 4.3358154 6.5414886 4.3358154 10.467606l0 59.212006c0 8.17572 -6.6277466 14.803467 -14.803467 14.803467l-497.4482 0c-8.175717 0 -14.803446 -6.6277466 -14.803446 -14.803467z" fill-rule="evenodd"/></g><g filter="url(#shadowFilter-p.2)"><use xlink:href="#p.2" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.2"><path fill="#b6d7a8" d="m4.8992295 13.393214l0 0c0 -5.2562323 4.2610183 -9.517251 9.51725 -9.517251l508.0206 0c2.52417 0 4.9448853 1.0027075 6.7297363 2.787538c1.7848511 1.7848306 2.7875366 4.205581 2.7875366 6.729713l0 38.06786c0 5.256233 -4.2609863 9.51725 -9.517273 9.51725l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.51725z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 13.393214l0 0c0 -5.2562323 4.2610183 -9.517251 9.51725 -9.517251l508.0206 0c2.52417 0 4.9448853 1.0027075 6.7297363 2.787538c1.7848511 1.7848306 2.7875366 4.205581 2.7875366 6.729713l0 38.06786c0 5.256233 -4.2609863 9.51725 -9.517273 9.51725l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.51725z" fill-rule="evenodd"/><path fill="#000000" d="m221.21878 39.34714l5.078125 -13.359375l1.78125 0l5.078125 13.359375l-1.734375 0l-3.671875 -9.984375l-0.53125 -1.4375l-0.0625 0l-0.53125 1.4375l-3.671875 9.984375l-1.734375 0zm5.78125 -3.671875l0 -1.5l3.078125 0l0.546875 1.5l-3.625 0zm-3.625 0l0.546875 -1.5l3.078125 0l0 1.5l-3.625 0zm11.327408 7.703125l0 -13.546875l1.515625 0l0 1.421875l0.078125 0q0.25 -0.453125 0.71875 -0.84375q0.484375 -0.390625 1.125 -0.625q0.640625 -0.25 1.390625 -0.25q1.3125 0 2.328125 0.65625q1.03125 0.65625 1.625 1.796875q0.609375 1.125 0.609375 2.609375q0 1.46875 -0.609375 2.609375q-0.59375 1.140625 -1.625 1.796875q-1.015625 0.640625 -2.328125 0.640625q-1.125 0 -1.984375 -0.515625q-0.859375 -0.53125 -1.25 -1.1875l-0.078125 0l0.078125 1.3125l0 4.125l-1.59375 0zm4.671875 -5.171875q0.8125 0 1.53125 -0.4375q0.71875 -0.4375 1.15625 -1.25q0.4375 -0.828125 0.4375 -1.921875q0 -1.125 -0.4375 -1.9375q-0.4375 -0.8125 -1.15625 -1.25q-0.71875 -0.4375 -1.53125 -0.4375q-0.828125 0 -1.546875 0.4375q-0.71875 0.4375 -1.171875 1.25q-0.4375 0.8125 -0.4375 1.9375q0 1.109375 0.4375 1.921875q0.453125 0.8125 1.171875 1.25q0.71875 0.4375 1.546875 0.4375zm6.372528 5.171875l0 -13.546875l1.515625 0l0 1.421875l0.078125 0q0.25 -0.453125 0.71875 -0.84375q0.484375 -0.390625 1.125 -0.625q0.640625 -0.25 1.390625 -0.25q1.3125 0 2.328125 0.65625q1.03125 0.65625 1.625 1.796875q0.609375 1.125 0.609375 2.609375q0 1.46875 -0.609375 2.609375q-0.59375 1.140625 -1.625 1.796875q-1.015625 0.640625 -2.328125 0.640625q-1.125 0 -1.984375 -0.515625q-0.859375 -0.53125 -1.25 -1.1875l-0.078125 0l0.078125 1.3125l0 4.125l-1.59375 0zm4.671875 -5.171875q0.8125 0 1.53125 -0.4375q0.71875 -0.4375 1.15625 -1.25q0.4375 -0.828125 0.4375 -1.921875q0 -1.125 -0.4375 -1.9375q-0.4375 -0.8125 -1.15625 -1.25q-0.71875 -0.4375 -1.53125 -0.4375q-0.828125 0 -1.546875 0.4375q-0.71875 0.4375 -1.171875 1.25q-0.4375 0.8125 -0.4375 1.9375q0 1.109375 0.4375 1.921875q0.453125 0.8125 1.171875 1.25q0.71875 0.4375 1.546875 0.4375zm6.372513 1.140625l0 -13.359375l1.59375 0l0 13.359375l-1.59375 0zm4.0927124 0l0 -9.515625l1.578125 0l0 9.515625l-1.578125 0zm0.78125 -11.265625q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.497986 11.5625q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.46875 0.609375 -2.59375q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.921875q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.859375q-1.03125 0.765625 -2.640625 0.765625zm8.827332 0q-1.0625 0 -1.875 -0.40625q-0.796875 -0.40625 -1.25 -1.125q-0.453125 -0.71875 -0.453125 -1.640625q0 -1.046875 0.53125 -1.765625q0.546875 -0.71875 1.453125 -1.078125q0.921875 -0.359375 2.015625 -0.359375q0.640625 0 1.171875 0.109375q0.546875 0.09375 0.9375 0.234375q0.40625 0.140625 0.625 0.265625l0 -0.578125q0 -1.078125 -0.765625 -1.703125q-0.765625 -0.640625 -1.875 -0.640625q-0.78125 0 -1.46875 0.34375q-0.671875 0.34375 -1.0625 0.953125l-1.203125 -0.890625q0.375 -0.5625 0.9375 -0.96875q0.5625 -0.40625 1.28125 -0.625q0.71875 -0.234375 1.515625 -0.234375q1.9375 0 3.03125 1.03125q1.109375 1.015625 1.109375 2.75l0 6.03125l-1.5 0l0 -1.359375l-0.078125 0q-0.25 0.40625 -0.703125 0.796875q-0.4375 0.375 -1.046875 0.609375q-0.609375 0.25 -1.328125 0.25zm0.140625 -1.390625q0.828125 0 1.5 -0.40625q0.6875 -0.421875 1.09375 -1.109375q0.421875 -0.6875 0.421875 -1.515625q-0.4375 -0.296875 -1.078125 -0.484375q-0.640625 -0.1875 -1.40625 -0.1875q-1.359375 0 -2.0 0.5625q-0.640625 0.5625 -0.640625 1.375q0 0.78125 0.59375 1.28125q0.609375 0.484375 1.515625 0.484375zm5.876068 -8.421875l5.578125 0l0 1.4375l-5.578125 0l0 -1.4375zm1.671875 7.015625l0 -9.703125l1.578125 0l0 9.3125q0 0.75 0.3125 1.15625q0.3125 0.40625 1.015625 0.40625q0.3125 0 0.578125 -0.09375q0.265625 -0.09375 0.46875 -0.21875l0 1.546875q-0.25 0.109375 -0.546875 0.171875q-0.28125 0.078125 -0.765625 0.078125q-1.1875 0 -1.921875 -0.703125q-0.71875 -0.703125 -0.71875 -1.953125zm6.0434875 2.5l0 -9.515625l1.578125 0l0 9.515625l-1.578125 0zm0.78125 -11.265625q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.576111 11.5625q-1.4375 0 -2.546875 -0.671875q-1.09375 -0.671875 -1.71875 -1.8125q-0.625 -1.15625 -0.625 -2.5625q0 -1.421875 0.625 -2.5625q0.625 -1.15625 1.71875 -1.828125q1.109375 -0.671875 2.546875 -0.671875q1.4375 0 2.53125 0.6875q1.109375 0.671875 1.734375 1.828125q0.625 1.140625 0.625 2.546875q0 1.40625 -0.625 2.5625q-0.625 1.140625 -1.734375 1.8125q-1.09375 0.671875 -2.53125 0.671875zm0 -1.4375q0.859375 0 1.609375 -0.421875q0.75 -0.4375 1.21875 -1.25q0.46875 -0.8125 0.46875 -1.9375q0 -1.140625 -0.46875 -1.953125q-0.46875 -0.8125 -1.21875 -1.234375q-0.75 -0.4375 -1.609375 -0.4375q-0.859375 0 -1.625 0.4375q-0.765625 0.421875 -1.234375 1.234375q-0.46875 0.8125 -0.46875 1.953125q0 1.125 0.46875 1.9375q0.46875 0.8125 1.234375 1.25q0.765625 0.421875 1.625 0.421875zm6.5418396 1.140625l0 -9.515625l1.515625 0l0 1.40625l0.078125 0q0.375 -0.703125 1.21875 -1.203125q0.84375 -0.5 1.859375 -0.5q1.75 0 2.625 1.015625q0.890625 1.015625 0.890625 2.703125l0 6.09375l-1.578125 0l0 -5.859375q0 -1.375 -0.671875 -1.9375q-0.65625 -0.578125 -1.703125 -0.578125q-0.78125 0 -1.375 0.4375q-0.59375 0.4375 -0.9375 1.125q-0.328125 0.6875 -0.328125 1.453125l0 5.359375l-1.59375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.3)"><use xlink:href="#p.3" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.3" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.3"><path fill="#c9daf8" d="m4.8992295 97.076035l0 0c0 -8.175713 6.627728 -14.803444 14.803446 -14.803444l147.81042 0c3.926117 0 7.691437 1.5596466 10.467621 4.3358307c2.776184 2.776184 4.3358307 6.5414963 4.3358307 10.467613l0 59.212006c0 8.17572 -6.6277313 14.803452 -14.803452 14.803452l-147.81042 0c-8.175717 0 -14.803446 -6.6277313 -14.803446 -14.803452z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 97.076035l0 0c0 -8.175713 6.627728 -14.803444 14.803446 -14.803444l147.81042 0c3.926117 0 7.691437 1.5596466 10.467621 4.3358307c2.776184 2.776184 4.3358307 6.5414963 4.3358307 10.467613l0 59.212006c0 8.17572 -6.6277313 14.803452 -14.803452 14.803452l-147.81042 0c-8.175717 0 -14.803446 -6.6277313 -14.803446 -14.803452z" fill-rule="evenodd"/><path fill="#000000" d="m66.37746 133.60204l0 -13.359367l4.015625 0q2.09375 0 3.609375 0.859375q1.515625 0.84375 2.328125 2.359375q0.828125 1.5 0.828125 3.46875q0 1.9531174 -0.828125 3.4531174q-0.8125 1.5 -2.328125 2.359375q-1.515625 0.859375 -3.609375 0.859375l-4.015625 0zm1.578125 -1.515625l2.4375 0q1.578125 0 2.734375 -0.59375q1.15625 -0.609375 1.78125 -1.765625q0.640625 -1.15625 0.640625 -2.7968674q0 -1.65625 -0.640625 -2.8125q-0.625 -1.15625 -1.78125 -1.75q-1.15625 -0.609375 -2.734375 -0.609375l-2.4375 0l0 10.328117zm15.163467 1.8125q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.3593674 0.5625 -2.5156174q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.5156174q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3124924l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.8593674q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125zm8.879303 -0.296875l-3.84375 -9.515617l1.703125 0l2.953125 7.6562424l0.03125 0l2.984375 -7.6562424l1.65625 0l-3.875 9.515617l-1.609375 0zm6.9486847 0l0 -9.515617l1.578125 0l0 9.515617l-1.578125 0zm0.78125 -11.265617q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.4979706 11.562492q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.4687424 0.609375 -2.5937424q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.9218674q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.859375q-1.03125 0.765625 -2.640625 0.765625zm10.014847 0q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.3593674 0.5625 -2.5156174q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.5156174q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3124924l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.8593674q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.4)"><use xlink:href="#p.4" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.4" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.4"><path fill="#c9daf8" d="m192.74174 100.78206l0 0c0 -7.7669296 6.296341 -14.063271 14.063278 -14.063271l311.08603 0c3.7298584 0 7.306885 1.4816589 9.944275 4.119034c2.637329 2.6373749 4.1190186 6.214424 4.1190186 9.944237l0 56.251404c0 7.7669373 -6.2963257 14.063278 -14.063293 14.063278l-311.08603 0c-7.7669373 0 -14.063278 -6.296341 -14.063278 -14.063278z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m192.74174 100.78206l0 0c0 -7.7669296 6.296341 -14.063271 14.063278 -14.063271l311.08603 0c3.7298584 0 7.306885 1.4816589 9.944275 4.119034c2.637329 2.6373749 4.1190186 6.214424 4.1190186 9.944237l0 56.251404c0 7.7669373 -6.2963257 14.063278 -14.063293 14.063278l-311.08603 0c-7.7669373 0 -14.063278 -6.296341 -14.063278 -14.063278z" fill-rule="evenodd"/></g><path fill="#a2c4c9" d="m203.9767 122.657425l0 0c0 -4.68219 3.7956696 -8.4778595 8.4778595 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.4778595 -3.7956543 -8.4778595 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m203.9767 122.657425l0 0c0 -4.68219 3.7956696 -8.4778595 8.4778595 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.4778595 -3.7956543 -8.4778595 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m221.41891 145.47264l4.359375 -11.453125l1.53125 0l4.359375 11.453125l-1.5 0l-3.140625 -8.5625l-0.453125 -1.234375l-0.0625 0l-0.453125 1.234375l-3.140625 8.5625l-1.5 0zm4.96875 -3.15625l0 -1.28125l2.640625 0l0.453125 1.28125l-3.09375 0zm-3.109375 0l0.46875 -1.28125l2.640625 0l0 1.28125l-3.109375 0zm9.3450165 3.15625l0 -1.359375q0.046875 -0.046875 0.375 -0.375q0.34375 -0.34375 0.84375 -0.84375q0.5 -0.515625 1.03125 -1.0625q0.546875 -0.546875 1.015625 -1.03125q0.484375 -0.5 0.75 -0.796875q0.5 -0.546875 0.796875 -0.9375q0.3125 -0.390625 0.453125 -0.765625q0.140625 -0.375 0.140625 -0.875q0 -0.46875 -0.25 -0.890625q-0.234375 -0.421875 -0.6875 -0.6875q-0.453125 -0.28125 -1.140625 -0.28125q-0.640625 0 -1.078125 0.28125q-0.4375 0.265625 -0.6875 0.640625q-0.234375 0.375 -0.3125 0.6875l-1.234375 -0.5q0.09375 -0.34375 0.34375 -0.75q0.25 -0.40625 0.65625 -0.78125q0.40625 -0.375 0.984375 -0.625q0.59375 -0.25 1.359375 -0.25q1.046875 0 1.8125 0.453125q0.76564026 0.4375 1.1875153 1.15625q0.421875 0.703125 0.421875 1.53125q0 0.71875 -0.28125 1.375q-0.265625 0.640625 -0.67189026 1.171875q-0.390625 0.53125 -0.796875 0.921875q-0.21875 0.203125 -0.59375 0.578125q-0.359375 0.375 -0.78125 0.796875q-0.40625 0.421875 -0.8125 0.828125q-0.390625 0.390625 -0.6875 0.703125q-0.296875 0.296875 -0.40625 0.390625l0 0l5.1562653 0l0 1.296875l-6.9062653 0zm8.978531 0l0 -11.453125l3.453125 0q1.796875 0 3.09375 0.734375q1.296875 0.71875 2.0 2.015625q0.703125 1.296875 0.703125 2.96875q0 1.6875 -0.703125 2.984375q-0.703125 1.28125 -2.0 2.015625q-1.296875 0.734375 -3.09375 0.734375l-3.453125 0zm1.359375 -1.296875l2.09375 0q1.34375 0 2.328125 -0.515625q1.0 -0.53125 1.546875 -1.515625q0.546875 -0.984375 0.546875 -2.40625q0 -1.40625 -0.546875 -2.390625q-0.546875 -1.0 -1.546875 -1.515625q-0.984375 -0.515625 -2.328125 -0.515625l-2.09375 0l0 8.859375zm9.888641 1.296875l0 -11.453125l3.859375 0q0.953125 0 1.765625 0.4375q0.828125 0.421875 1.3125 1.1875q0.484375 0.75 0.484375 1.765625q0 0.984375 -0.484375 1.765625q-0.484375 0.765625 -1.3125 1.203125q-0.8125 0.421875 -1.765625 0.421875l-3.140625 0l0 -1.296875l3.171875 0q0.671875 0 1.140625 -0.3125q0.484375 -0.3125 0.75 -0.78125q0.265625 -0.484375 0.265625 -1.0q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m284.8796 122.657425l0 0c0 -4.68219 3.7956543 -8.4778595 8.477844 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.477844 -3.7956543 -8.477844 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m284.8796 122.657425l0 0c0 -4.68219 3.7956543 -8.4778595 8.477844 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.477844 -3.7956543 -8.477844 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m308.7287 145.47264l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9180298 0l0 -11.453125l6.625 0l0 1.296875l-5.265625 0l0 10.15625l-1.359375 0zm0.71875 -4.953125l0 -1.28125l5.390625 0l0 1.28125l-5.390625 0zm7.777252 4.953125l0 -11.453125l3.859375 0q0.953125 0 1.765625 0.4375q0.828125 0.421875 1.3125 1.1875q0.484375 0.75 0.484375 1.765625q0 0.984375 -0.484375 1.765625q-0.484375 0.765625 -1.3125 1.203125q-0.8125 0.421875 -1.765625 0.421875l-3.140625 0l0 -1.296875l3.171875 0q0.671875 0 1.140625 -0.3125q0.484375 -0.3125 0.75 -0.78125q0.265625 -0.484375 0.265625 -1.0q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m365.78247 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m365.78247 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m390.90356 145.47264l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9180298 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9200134 0l0 -11.453125l3.453125 0q1.796875 0 3.09375 0.734375q1.296875 0.71875 2.0 2.015625q0.703125 1.296875 0.703125 2.96875q0 1.6875 -0.703125 2.984375q-0.703125 1.28125 -2.0 2.015625q-1.296875 0.734375 -3.09375 0.734375l-3.453125 0zm1.359375 -1.296875l2.09375 0q1.34375 0 2.328125 -0.515625q1.0 -0.53125 1.546875 -1.515625q0.546875 -0.984375 0.546875 -2.40625q0 -1.40625 -0.546875 -2.390625q-0.546875 -1.0 -1.546875 -1.515625q-0.984375 -0.515625 -2.328125 -0.515625l-2.09375 0l0 8.859375z" fill-rule="nonzero"/><g filter="url(#shadowFilter-p.5)"><use xlink:href="#p.5" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.5" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.5"><path fill="#6d9eeb" d="m4.8992295 317.66617l0 0c0 -5.2562256 4.2610183 -9.517242 9.51725 -9.517242l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.205597 2.7875366 6.729706l0 38.06787c0 5.2562256 -4.2609863 9.517242 -9.517273 9.517242l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 317.66617l0 0c0 -5.2562256 4.2610183 -9.517242 9.51725 -9.517242l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.205597 2.7875366 6.729706l0 38.06787c0 5.2562256 -4.2609863 9.517242 -9.517273 9.517242l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m252.81201 342.56012l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm7.242401 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm8.549774 1.21875q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.0817566 -8.40625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875z" fill-rule="nonzero"/></g><path fill="#a2c4c9" d="m141.82906 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m141.82906 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m162.77774 250.34612q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.609375 -0.953125 -1.421875q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71875q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.5q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3125q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.59375l1.15625 0l3.265625 8.59375l-1.109375 0l-2.375 -6.421875l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.421875l-1.109375 0zm3.71875 -2.359375l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm8.400681 2.359375l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0zm8.862289 7.625l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0z" fill-rule="nonzero"/><path fill="#000000" d="m161.77573 264.3461q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.609375 -0.9375 -1.421875q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.95310974q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46873474q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.421875 1.703125 0.421875q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.4375q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.4375 -0.90625 0.796875q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm4.537689 -0.1875l0 -8.593735l1.03125 0l0 8.593735l-1.03125 0zm2.641388 0l0 -6.125l1.015625 0l0 6.125l-1.015625 0zm0.5 -7.25q-0.296875 0 -0.515625 -0.21875q-0.21875 -0.21875 -0.21875 -0.515625q0 -0.31248474 0.21875 -0.51560974q0.21875 -0.21875 0.515625 -0.21875q0.3125 0 0.515625 0.21875q0.21875 0.203125 0.21875 0.51560974q0 0.296875 -0.21875 0.515625q-0.203125 0.21875 -0.515625 0.21875zm4.7770233 7.4375q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.90625l0.046875 0q0.234375 -0.4375 0.78125 -0.765625q0.546875 -0.328125 1.1875 -0.328125q1.140625 0 1.703125 0.65625q0.578125 0.65625 0.578125 1.734375l0 3.921875l-1.03125 0l0 -3.765625q0 -0.890625 -0.421875 -1.25q-0.421875 -0.375 -1.09375 -0.375q-0.515625 0 -0.90625 0.28125q-0.375 0.28125 -0.59375 0.734375q-0.203125 0.4375 -0.203125 0.9375l0 3.4375l-1.03125 0zm6.0257874 -6.125l3.578125 0l0 0.921875l-3.578125 0l0 -0.921875zm1.0625 4.515625l0 -6.234375l1.015625 0l0 5.984375q0 0.484375 0.1875 0.75q0.203125 0.25 0.671875 0.25q0.203125 0 0.359375 -0.046875q0.171875 -0.0625 0.3125 -0.15625l0 1.0q-0.15625 0.078125 -0.34375 0.109375q-0.1875 0.046875 -0.5 0.046875q-0.765625 0 -1.234375 -0.4375q-0.46875 -0.453125 -0.46875 -1.265625z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m77.69106 235.04298l0 0c0 -4.8590393 3.9390335 -8.798065 8.798073 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499481 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798073 -3.9390259 -8.798073 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m77.69106 235.04298l0 0c0 -4.8590393 3.9390335 -8.798065 8.798073 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499481 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798073 -3.9390259 -8.798073 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m101.24976 257.3461q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.60935974 -0.953125 -1.4218597q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71873474q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.49998474q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3124847q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.593735l1.15625 0l3.265625 8.593735l-1.109375 0l-2.375 -6.4218597l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.4218597l-1.109375 0zm3.71875 -2.3593597l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm7.4150543 2.3593597l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m205.96704 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.85266 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m205.96704 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.85266 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m226.91573 250.34612q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.609375 -0.953125 -1.421875q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71875q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.5q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3125q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.59375l1.15625 0l3.265625 8.59375l-1.109375 0l-2.375 -6.421875l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.421875l-1.109375 0zm3.71875 -2.359375l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm8.400681 2.359375l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0zm8.862289 7.625l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0z" fill-rule="nonzero"/><path fill="#000000" d="m222.3267 264.3461q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.515625 -0.6875 -1.28125l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.5 1.328125 0.5q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.5q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.82810974q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.68748474q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.140625q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm6.869049 0q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0zm6.5165253 0l-2.484375 -6.125l1.09375 0l1.90625 4.921875l0.015625 0l1.921875 -4.921875l1.0625 0l-2.484375 6.125l-1.03125 0zm6.9099274 0.1875q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m270.105 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499329 1.6499634 2.5768738 3.8877869 2.5768738 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85266 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m270.105 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499329 1.6499634 2.5768738 3.8877869 2.5768738 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85266 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m281.83264 250.34612q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.515625 -0.6875 -1.28125l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.5 1.328125 0.5q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.5q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.828125q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.6875q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.140625q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm6.869049 0q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm6.862915 0q-0.90625 0 -1.609375 -0.421875q-0.703125 -0.421875 -1.09375 -1.15625q-0.390625 -0.75 -0.390625 -1.671875q0 -0.9375 0.390625 -1.65625q0.390625 -0.734375 1.09375 -1.15625q0.703125 -0.4375 1.609375 -0.4375q1.03125 0 1.6875 0.484375q0.671875 0.46875 0.953125 1.203125l-0.921875 0.390625q-0.234375 -0.5625 -0.6875 -0.859375q-0.453125 -0.296875 -1.078125 -0.296875q-0.546875 0 -1.015625 0.296875q-0.453125 0.28125 -0.75 0.8125q-0.28125 0.515625 -0.28125 1.21875q0 0.703125 0.28125 1.234375q0.296875 0.515625 0.75 0.8125q0.46875 0.28125 1.015625 0.28125q0.640625 0 1.109375 -0.296875q0.46875 -0.296875 0.703125 -0.859375l0.90625 0.390625q-0.296875 0.703125 -0.96875 1.203125q-0.671875 0.484375 -1.703125 0.484375zm5.989563 0q-1.125 0 -1.703125 -0.65625q-0.5625 -0.65625 -0.5625 -1.796875l0 -3.859375l1.03125 0l0 3.703125q0 0.90625 0.40625 1.296875q0.421875 0.390625 1.046875 0.390625q0.546875 0 0.9375 -0.28125q0.40625 -0.28125 0.609375 -0.71875q0.21875 -0.453125 0.21875 -0.921875l0 -3.46875l1.015625 0l0 6.125l-0.96875 0l0 -0.890625l-0.046875 0q-0.15625 0.296875 -0.46875 0.546875q-0.296875 0.234375 -0.6875 0.375q-0.390625 0.15625 -0.828125 0.15625zm4.513275 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0zm4.6094055 0l0 -6.125l1.015625 0l0 6.125l-1.015625 0zm0.5 -7.25q-0.296875 0 -0.515625 -0.21875q-0.21875 -0.21875 -0.21875 -0.515625q0 -0.3125 0.21875 -0.515625q0.21875 -0.21875 0.515625 -0.21875q0.3125 0 0.515625 0.21875q0.21875 0.203125 0.21875 0.515625q0 0.296875 -0.21875 0.515625q-0.203125 0.21875 -0.515625 0.21875zm1.6364136 1.125l3.578125 0l0 0.921875l-3.578125 0l0 -0.921875zm1.0625 4.515625l0 -6.234375l1.015625 0l0 5.984375q0 0.484375 0.1875 0.75q0.203125 0.25 0.671875 0.25q0.203125 0 0.359375 -0.046875q0.171875 -0.0625 0.3125 -0.15625l0 1.0q-0.15625 0.078125 -0.34375 0.109375q-0.1875 0.046875 -0.5 0.046875q-0.765625 0 -1.234375 -0.4375q-0.46875 -0.453125 -0.46875 -1.265625zm5.549408 3.890625q-0.046875 0.078125 -0.09375 0.171875q-0.046875 0.109375 -0.0625 0.140625l-1.046875 0q0.0625 -0.15625 0.140625 -0.34375q0.09375 -0.1875 0.21875 -0.453125q0.0625 -0.15625 0.125 -0.296875q0.0625 -0.125 0.140625 -0.28125q0.078125 -0.15625 0.1875 -0.40625l0.84375 -1.828125l0.21875 -0.5625l1.859375 -4.546875l1.09375 0l-3.171875 7.3125q-0.046875 0.109375 -0.15625 0.34375q-0.09375 0.234375 -0.1875 0.453125q-0.078125 0.21875 -0.109375 0.296875zm0.21875 -2.53125l-2.59375 -5.875l1.109375 0l1.921875 4.546875l0.09375 0l-0.53125 1.328125z" fill-rule="nonzero"/><path fill="#000000" d="m277.8184 264.1586l0 -8.593735l1.484375 0l2.734375 7.0468597l0.046875 0l2.734375 -7.0468597l1.484375 0l0 8.593735l-1.015625 0l0 -5.453125l0.046875 -1.640625l-0.046875 0l-2.8125 7.09375l-0.828125 0l-2.8125 -7.09375l-0.046875 0l0.046875 1.640625l0 5.453125l-1.015625 0zm11.977722 0.1875q-0.6875 0 -1.203125 -0.265625q-0.515625 -0.265625 -0.8125 -0.71875q-0.296875 -0.46875 -0.296875 -1.046875q0 -0.671875 0.34375 -1.125q0.359375 -0.46875 0.9375 -0.703125q0.59375 -0.234375 1.296875 -0.234375q0.40625 0 0.75 0.0625q0.359375 0.0625 0.609375 0.15625q0.265625 0.09375 0.390625 0.171875l0 -0.375q0 -0.6875 -0.5 -1.09375q-0.484375 -0.421875 -1.1875 -0.421875q-0.5 0 -0.9375 0.234375q-0.4375 0.21875 -0.6875 0.609375l-0.78125 -0.578125q0.25 -0.359375 0.609375 -0.625q0.359375 -0.265625 0.8125 -0.40625q0.46875 -0.140625 0.984375 -0.140625q1.25 0 1.953125 0.671875q0.71875 0.65625 0.71875 1.765625l0 3.875l-0.984375 0l0 -0.875l-0.046875 0q-0.15625 0.265625 -0.453125 0.515625q-0.28125 0.234375 -0.671875 0.390625q-0.375 0.15625 -0.84375 0.15625zm0.09375 -0.890625q0.53125 0 0.96875 -0.265625q0.4375 -0.265625 0.6875 -0.703125q0.265625 -0.453125 0.265625 -0.984375q-0.265625 -0.1875 -0.6875 -0.296875q-0.40625 -0.125 -0.90625 -0.125q-0.875 0 -1.28125 0.359375q-0.40625 0.359375 -0.40625 0.875q0 0.515625 0.375 0.828125q0.390625 0.3125 0.984375 0.3125zm4.30954 0.703125l0 -6.125l0.984375 0l0 0.90625l0.046875 0q0.234375 -0.4375 0.78125 -0.765625q0.546875 -0.328125 1.1875 -0.328125q1.140625 0 1.703125 0.65625q0.578125 0.65625 0.578125 1.734375l0 3.921875l-1.03125 0l0 -3.765625q0 -0.890625 -0.421875 -1.25q-0.421875 -0.375 -1.09375 -0.375q-0.515625 0 -0.90625 0.28125q-0.375 0.28125 -0.59375 0.734375q-0.203125 0.4375 -0.203125 0.9375l0 3.4375l-1.03125 0zm8.640778 0.1875q-0.6875 0 -1.203125 -0.265625q-0.515625 -0.265625 -0.8125 -0.71875q-0.296875 -0.46875 -0.296875 -1.046875q0 -0.671875 0.34375 -1.125q0.359375 -0.46875 0.9375 -0.703125q0.59375 -0.234375 1.296875 -0.234375q0.40625 0 0.75 0.0625q0.359375 0.0625 0.609375 0.15625q0.265625 0.09375 0.390625 0.171875l0 -0.375q0 -0.6875 -0.5 -1.09375q-0.484375 -0.421875 -1.1875 -0.421875q-0.5 0 -0.9375 0.234375q-0.4375 0.21875 -0.6875 0.609375l-0.78125 -0.578125q0.25 -0.359375 0.609375 -0.625q0.359375 -0.265625 0.8125 -0.40625q0.46875 -0.140625 0.984375 -0.140625q1.25 0 1.953125 0.671875q0.71875 0.65625 0.71875 1.765625l0 3.875l-0.984375 0l0 -0.875l-0.046875 0q-0.15625 0.265625 -0.453125 0.515625q-0.28125 0.234375 -0.671875 0.390625q-0.375 0.15625 -0.84375 0.15625zm0.09375 -0.890625q0.53125 0 0.96875 -0.265625q0.4375 -0.265625 0.6875 -0.703125q0.265625 -0.453125 0.265625 -0.984375q-0.265625 -0.1875 -0.6875 -0.296875q-0.40625 -0.125 -0.90625 -0.125q-0.875 0 -1.28125 0.359375q-0.40625 0.359375 -0.40625 0.875q0 0.515625 0.375 0.828125q0.390625 0.3125 0.984375 0.3125zm6.937042 3.484375q-0.78125 0 -1.34375 -0.265625q-0.546875 -0.25 -0.890625 -0.65625q-0.34375 -0.390625 -0.484375 -0.796875l0.9375 -0.390625q0.1875 0.5 0.640625 0.84375q0.46875 0.34375 1.140625 0.34375q0.96875 0 1.484375 -0.5625q0.515625 -0.546875 0.515625 -1.546875l0 -0.6875l-0.046875 0q-0.296875 0.453125 -0.84375 0.765625q-0.53125 0.296875 -1.25 0.296875q-0.78125 0 -1.4375 -0.40625q-0.65625 -0.421875 -1.046875 -1.140625q-0.375 -0.71875 -0.375 -1.671875q0 -0.953125 0.375 -1.671875q0.390625 -0.734375 1.046875 -1.140625q0.65625 -0.40625 1.4375 -0.40625q0.71875 0 1.25 0.3125q0.546875 0.296875 0.84375 0.75l0.046875 0l0 -0.875l0.96875 0l0 5.890625q0 1.015625 -0.390625 1.6875q-0.390625 0.671875 -1.0625 1.0q-0.65625 0.328125 -1.515625 0.328125zm0 -3.578125q0.546875 0 1.0 -0.265625q0.453125 -0.28125 0.71875 -0.796875q0.28125 -0.515625 0.28125 -1.234375q0 -0.75 -0.28125 -1.25q-0.265625 -0.515625 -0.71875 -0.78125q-0.453125 -0.265625 -1.0 -0.265625q-0.53125 0 -1.0 0.28125q-0.453125 0.265625 -0.734375 0.78125q-0.265625 0.5 -0.265625 1.234375q0 0.734375 0.265625 1.25q0.28125 0.5 0.734375 0.78125q0.46875 0.265625 1.0 0.265625zm7.15094 0.984375q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m13.553075 235.04298l0 0c0 -4.8590393 3.9390326 -8.798065 8.798076 -8.798065l43.85267 0c2.3333893 0 4.5712204 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.5768967 3.8877869 2.5768967 6.221176l0 35.19124c0 4.85907 -3.9390335 8.798096 -8.798073 8.798096l-43.85267 0c-4.859043 0 -8.798076 -3.9390259 -8.798076 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.553075 235.04298l0 0c0 -4.8590393 3.9390326 -8.798065 8.798076 -8.798065l43.85267 0c2.3333893 0 4.5712204 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.5768967 3.8877869 2.5768967 6.221176l0 35.19124c0 4.85907 -3.9390335 8.798096 -8.798073 8.798096l-43.85267 0c-4.859043 0 -8.798076 -3.9390259 -8.798076 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m27.098484 257.1586l0 -8.593735l1.03125 0l0 7.6249847l3.75 0l0 0.96875l-4.78125 0zm5.7017975 0l0 -1.015625q0.03125 -0.046875 0.28125 -0.29685974q0.25 -0.25 0.625 -0.625q0.375 -0.375 0.78125 -0.78125q0.40625 -0.421875 0.75 -0.78125q0.359375 -0.375 0.5625 -0.609375q0.375 -0.40625 0.59375 -0.6875q0.234375 -0.296875 0.34375 -0.578125q0.109375 -0.28125 0.109375 -0.65625q0 -0.359375 -0.1875 -0.671875q-0.171875 -0.3125 -0.515625 -0.515625q-0.34375 -0.203125 -0.859375 -0.203125q-0.46875 0 -0.8125 0.203125q-0.328125 0.203125 -0.515625 0.484375q-0.171875 0.28125 -0.234375 0.5l-0.921875 -0.359375q0.078125 -0.265625 0.265625 -0.5625q0.1875 -0.3125 0.484375 -0.59375q0.3125 -0.28125 0.75 -0.46875q0.4375 -0.1875 1.015625 -0.1875q0.78125 0 1.34375 0.34375q0.578125 0.328125 0.890625 0.859375q0.328125 0.53125 0.328125 1.15625q0 0.53125 -0.203125 1.03125q-0.203125 0.484375 -0.5 0.875q-0.296875 0.390625 -0.609375 0.703125q-0.15625 0.15625 -0.4375 0.4375q-0.28125 0.265625 -0.59375 0.59375q-0.3125 0.3125 -0.609375 0.609375q-0.296875 0.296875 -0.515625 0.53125q-0.21875 0.21873474 -0.3125 0.29685974l0 0l3.875 0l0 0.96875l-5.171875 0zm10.749542 0.1875q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.60935974 -0.9375 -1.4218597q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.953125q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46875q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.42185974 1.703125 0.42185974q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.43748474q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.43748474 -0.90625 0.79685974q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm4.0951843 -0.1875l3.25 -8.593735l1.15625 0l3.265625 8.593735l-1.109375 0l-2.375 -6.4218597l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.4218597l-1.109375 0zm3.71875 -2.3593597l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm7.4150543 2.3593597l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m334.243 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85269 0c2.333374 0 4.5711975 0.92692566 6.221161 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85269 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m334.243 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85269 0c2.333374 0 4.5711975 0.92692566 6.221161 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85269 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m357.28073 257.3461q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.51560974 -0.6875 -1.2812347l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.49998474 1.328125 0.49998474q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.49998474q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.828125q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.6875q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.1406097q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm4.337799 -0.1875l0 -8.593735l2.59375 0q1.34375 0 2.3125 0.546875q0.984375 0.546875 1.5 1.515625q0.53125 0.96875 0.53125 2.234375q0 1.265625 -0.53125 2.234375q-0.515625 0.95310974 -1.5 1.5156097q-0.96875 0.546875 -2.3125 0.546875l-2.59375 0zm1.03125 -0.96875l1.5625 0q1.015625 0 1.75 -0.39060974q0.75 -0.390625 1.15625 -1.125q0.40625 -0.75 0.40625 -1.8125q0 -1.0625 -0.40625 -1.796875q-0.40625 -0.75 -1.15625 -1.140625q-0.734375 -0.390625 -1.75 -0.390625l-1.5625 0l0 6.6562347zm7.4048157 0.96875l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m398.381 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.947144 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390564 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m398.381 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.947144 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390564 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m405.1216 257.1586l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.453125 -0.171875 0.90625q-0.171875 0.453125 -0.53125 0.828125q-0.34375 0.359375 -0.890625 0.59375q-0.546875 0.21875 -1.296875 0.21875l-2.140625 0l0 -0.953125l2.296875 0q0.453125 0 0.828125 -0.203125q0.390625 -0.203125 0.625 -0.5625q0.25 -0.359375 0.25 -0.828125q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0zm2.078125 -3.9374847l1.125 -0.0625l2.703125 3.9531097l0 0.046875l-1.203125 0l-2.625 -3.9374847zm4.9539185 3.9374847l0 -8.593735l4.96875 0l0 0.96875l-3.9375 0l0 7.6249847l-1.03125 0zm0.546875 -3.7187347l0 -0.96875l4.046875 0l0 0.96875l-4.046875 0zm9.73642 3.9062347q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.60935974 -0.9375 -1.4218597q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.953125q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46875q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.42185974 1.703125 0.42185974q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.43748474q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.43748474 -0.90625 0.79685974q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm7.2457886 0q-0.921875 0 -1.640625 -0.421875q-0.703125 -0.4375 -1.109375 -1.1718597q-0.390625 -0.734375 -0.390625 -1.65625q0 -0.90625 0.390625 -1.640625q0.40625 -0.75 1.109375 -1.171875q0.71875 -0.4375 1.640625 -0.4375q0.921875 0 1.625 0.4375q0.71875 0.4375 1.109375 1.1875q0.40625 0.734375 0.40625 1.625q0 0.921875 -0.40625 1.65625q-0.390625 0.73435974 -1.109375 1.1718597q-0.703125 0.421875 -1.625 0.421875zm0 -0.921875q0.546875 0 1.03125 -0.265625q0.484375 -0.28123474 0.78125 -0.79685974q0.3125 -0.53125 0.3125 -1.265625q0 -0.734375 -0.3125 -1.25q-0.296875 -0.53125 -0.78125 -0.796875q-0.484375 -0.28125 -1.03125 -0.28125q-0.546875 0 -1.046875 0.28125q-0.484375 0.265625 -0.796875 0.796875q-0.296875 0.515625 -0.296875 1.25q0 0.734375 0.296875 1.265625q0.3125 0.515625 0.796875 0.79685974q0.5 0.265625 1.046875 0.265625zm4.203949 0.734375l0 -6.1249847l0.984375 0l0 0.90625l0.046875 0q0.15625 -0.296875 0.453125 -0.546875q0.296875 -0.25 0.671875 -0.390625q0.375 -0.15625 0.78125 -0.15625q0.6875 0 1.171875 0.328125q0.5 0.328125 0.703125 0.859375q0.3125 -0.515625 0.84375 -0.84375q0.53125 -0.34375 1.265625 -0.34375q1.09375 0 1.609375 0.671875q0.515625 0.65625 0.515625 1.71875l0 3.9218597l-1.015625 0l0 -3.7656097q0 -0.890625 -0.359375 -1.25q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.84375 0.28125q-0.359375 0.265625 -0.578125 0.703125q-0.203125 0.4375 -0.203125 0.953125l0 3.4531097l-1.015625 0l0 -3.7499847q0 -0.890625 -0.359375 -1.265625q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.828125 0.28125q-0.359375 0.265625 -0.578125 0.71875q-0.203125 0.4375 -0.203125 0.953125l0 3.4374847l-1.03125 0zm10.500061 0l0 -6.1249847l0.984375 0l0 0.90625l0.046875 0q0.15625 -0.296875 0.453125 -0.546875q0.296875 -0.25 0.671875 -0.390625q0.375 -0.15625 0.78125 -0.15625q0.6875 0 1.171875 0.328125q0.5 0.328125 0.703125 0.859375q0.3125 -0.515625 0.84375 -0.84375q0.53125 -0.34375 1.265625 -0.34375q1.09375 0 1.609375 0.671875q0.515625 0.65625 0.515625 1.71875l0 3.9218597l-1.015625 0l0 -3.7656097q0 -0.890625 -0.359375 -1.25q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.84375 0.28125q-0.359375 0.265625 -0.578125 0.703125q-0.203125 0.4375 -0.203125 0.953125l0 3.4531097l-1.015625 0l0 -3.7499847q0 -0.890625 -0.359375 -1.265625q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.828125 0.28125q-0.359375 0.265625 -0.578125 0.71875q-0.203125 0.4375 -0.203125 0.953125l0 3.4374847l-1.03125 0z" fill-rule="nonzero"/><g filter="url(#shadowFilter-p.6)"><use xlink:href="#p.6" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.6" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.6"><path fill="#f6b26b" d="m1.2233764 468.88928l0 0c0 -5.2562256 4.2610183 -9.517242 9.517251 -9.517242l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729721 2.7875366c1.7848206 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.261017 9.517242 -9.517258 9.517242l-235.35919 0c-5.2562327 0 -9.517251 -4.2609863 -9.517251 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1.2233764 468.88928l0 0c0 -5.2562256 4.2610183 -9.517242 9.517251 -9.517242l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729721 2.7875366c1.7848206 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.261017 9.517242 -9.517258 9.517242l-235.35919 0c-5.2562327 0 -9.517251 -4.2609863 -9.517251 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m99.084404 494.0332q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm9.645386 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.6260223 0.96875l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.980629 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.27002 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.706024 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.376007 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210052 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383011 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.7)"><use xlink:href="#p.7" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.7" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.7"><path fill="#f6b26b" d="m4.684006 544.50085l0 0c0 -5.2562866 4.2610183 -9.517273 9.51725 -9.517273l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729706 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2610016 9.517273 -9.517242 9.517273l-235.35919 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.684006 544.50085l0 0c0 -5.2562866 4.2610183 -9.517273 9.51725 -9.517273l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729706 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2610016 9.517273 -9.517242 9.517273l-235.35919 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m112.98605 569.3948l0 -11.453125l3.875 0q0.953125 0 1.765625 0.4375q0.8125 0.421875 1.296875 1.1875q0.484375 0.75 0.484375 1.765625q0 0.609375 -0.234375 1.21875q-0.21875 0.59375 -0.6875 1.09375q-0.46875 0.484375 -1.203125 0.78125q-0.71875 0.296875 -1.734375 0.296875l-2.84375 0l0 -1.265625l3.078125 0q0.578125 0 1.09375 -0.265625q0.515625 -0.28125 0.828125 -0.75q0.328125 -0.484375 0.328125 -1.109375q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0zm2.765625 -5.25l1.484375 -0.078125l3.625 5.265625l0 0.0625l-1.609375 0l-3.5 -5.25zm8.794525 5.5q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm9.150391 1.1875q-1.109375 0 -2.0 -0.546875q-0.890625 -0.5625 -1.40625 -1.53125q-0.5 -0.984375 -0.5 -2.25q0 -1.265625 0.5 -2.234375q0.515625 -0.984375 1.40625 -1.546875q0.890625 -0.5625 2.0 -0.5625q0.65625 0 1.1875 0.21875q0.546875 0.203125 0.953125 0.546875q0.421875 0.328125 0.640625 0.71875l0.0625 0l-0.0625 -1.140625l0 -3.375l1.359375 0l0 11.453125l-1.296875 0l0 -1.203125l-0.0625 0q-0.21875 0.375 -0.640625 0.71875q-0.40625 0.328125 -0.953125 0.53125q-0.53125 0.203125 -1.1875 0.203125zm0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.625 -0.390625 1.0 -1.078125q0.390625 -0.703125 0.390625 -1.65625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -1.0 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.703125 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.65625q0 0.9375 0.375 1.640625q0.390625 0.703125 1.0 1.09375q0.625 0.375 1.328125 0.375zm6.147644 0.96875l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm6.4891357 9.90625q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.8)"><use xlink:href="#p.8" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.8" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.8"><path fill="#1155cc" d="m274.31787 468.88928l0 0c0 -5.2562256 4.261017 -9.517242 9.517242 -9.517242l234.94974 0c2.52417 0 4.9448853 1.0026855 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.2610474 9.517242 -9.517273 9.517242l-234.94974 0c-5.2562256 0 -9.517242 -4.2609863 -9.517242 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m274.31787 468.88928l0 0c0 -5.2562256 4.261017 -9.517242 9.517242 -9.517242l234.94974 0c2.52417 0 4.9448853 1.0026855 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.2610474 9.517242 -9.517273 9.517242l-234.94974 0c-5.2562256 0 -9.517242 -4.2609863 -9.517242 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m344.86313 493.7832l-4.0625 -11.453125l1.515625 0l2.828125 8.296875l0.375 1.140625l0.078125 0l0.390625 -1.140625l2.96875 -8.296875l1.5 0l-4.1875 11.453125l-1.40625 0zm6.635376 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.9512634 2.390625q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm8.536652 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.761627 0.9375l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm12.40863 0.25q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm9.645386 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.6260376 0.96875l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.9806213 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.27002 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.706024 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.376007 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210205 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.3829956 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.9)"><use xlink:href="#p.9" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.9" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.9"><path fill="#1155cc" d="m277.77325 544.50085l0 0c0 -5.2562866 4.261017 -9.517273 9.517242 -9.517273l235.35922 0c2.524109 0 4.9448853 1.0026855 6.7296753 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2609863 9.517273 -9.517212 9.517273l-235.35922 0c-5.2562256 0 -9.517242 -4.2610474 -9.517242 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m277.77325 544.50085l0 0c0 -5.2562866 4.261017 -9.517273 9.517242 -9.517273l235.35922 0c2.524109 0 4.9448853 1.0026855 6.7296753 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2609863 9.517273 -9.517212 9.517273l-235.35922 0c-5.2562256 0 -9.517242 -4.2610474 -9.517242 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m348.32324 569.3948l-4.0625 -11.453125l1.515625 0l2.828125 8.296875l0.375 1.140625l0.078125 0l0.390625 -1.140625l2.96875 -8.296875l1.5 0l-4.1875 11.453125l-1.40625 0zm6.635376 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.9512634 2.390625q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm8.536652 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.761627 0.9375l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm7.3536377 0l0 -11.453125l1.359375 0l0 10.15625l5.0 0l0 1.296875l-6.359375 0zm8.003387 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.976013 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.5 -1.65625l0 -1.75l4.6875 -4.75l1.734375 0l0 0.0625l-6.421875 6.4375zm4.890625 1.65625l-2.953125 -4.390625l0.96875 -0.9375l3.609375 5.265625l0 0.0625l-1.625 0zm6.6670227 0l0 -11.453125l1.359375 0l0 10.15625l5.0 0l0 1.296875l-6.359375 0zm10.472137 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm7.9297485 3.984375q-0.046875 0.109375 -0.109375 0.234375q-0.0625 0.140625 -0.078125 0.171875l-1.40625 0q0.09375 -0.203125 0.203125 -0.453125q0.109375 -0.25 0.265625 -0.59375q0.09375 -0.21875 0.171875 -0.390625q0.09375 -0.171875 0.1875 -0.390625q0.109375 -0.21875 0.25 -0.53125l1.125 -2.4375l0.296875 -0.75l2.46875 -6.0625l1.46875 0l-4.21875 9.734375q-0.0625 0.15625 -0.203125 0.46875q-0.140625 0.3125 -0.265625 0.59375q-0.109375 0.296875 -0.15625 0.40625zm0.296875 -3.390625l-3.453125 -7.8125l1.46875 0l2.5625 6.0625l0.140625 0l-0.71875 1.75zm9.048004 0.59375q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383026 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><path fill="#a2c4c9" d="m462.61346 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.947144 0c2.333374 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m462.61346 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.947144 0c2.333374 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m490.619 257.23672q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875zm2.765625 0q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875zm2.75 0q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m446.68536 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m446.68536 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m480.01547 145.56639q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875zm3.6875 0q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875zm3.671875 0q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m268.4268 284.34842l0 23.811005" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m268.4268 284.34842l0 23.811005" fill-rule="evenodd"/><g filter="url(#shadowFilter-p.10)"><use xlink:href="#p.10" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.10" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.10"><path fill="#3c78d8" d="m4.8992295 393.27774l0 0c0 -5.256256 4.2610183 -9.517273 9.51725 -9.517273l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848511 2.7875366 4.205597 2.7875366 6.7297363l0 38.06784c0 5.2562256 -4.2609863 9.517273 -9.517273 9.517273l-508.0206 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 393.27774l0 0c0 -5.256256 4.2610183 -9.517273 9.51725 -9.517273l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848511 2.7875366 4.205597 2.7875366 6.7297363l0 38.06784c0 5.2562256 -4.2609863 9.517273 -9.517273 9.517273l-508.0206 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m220.21194 418.17166l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm9.133026 0.25q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm6.481018 -0.25l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm9.33577 0l0 -10.640625l1.359375 0l0 10.640625l-1.359375 0zm-3.203125 -10.15625l0 -1.296875l7.7656097 0l0 1.296875l-7.7656097 0zm8.241745 10.15625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm8.273773 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.7616577 0.9375l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm11.739746 0.25q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.6442566 3.203125l0 -11.609375l1.28125 0l0 1.21875l0.078125 0q0.21875 -0.390625 0.625 -0.71875q0.40625 -0.34375 0.953125 -0.546875q0.546875 -0.21875 1.203125 -0.21875q1.109375 0 1.984375 0.5625q0.890625 0.5625 1.40625 1.546875q0.515625 0.96875 0.515625 2.234375q0 1.265625 -0.515625 2.25q-0.515625 0.96875 -1.40625 1.53125q-0.875 0.546875 -1.984375 0.546875q-0.984375 0 -1.71875 -0.4375q-0.734375 -0.453125 -1.0625 -1.015625l-0.078125 0l0.078125 1.125l0 3.53125l-1.359375 0zm4.0 -4.421875q0.703125 0 1.3125 -0.375q0.609375 -0.390625 0.984375 -1.09375q0.390625 -0.703125 0.390625 -1.640625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -0.984375 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.71875 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.390625 0.6875 -0.390625 1.65625q0 0.953125 0.390625 1.65625q0.390625 0.6875 1.0 1.078125q0.609375 0.375 1.328125 0.375zm9.382019 1.21875q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.626007 0.96875l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875z" fill-rule="nonzero"/></g><path fill="#000000" fill-opacity="0.0" d="m268.4268 365.25128l0 18.519684" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m268.4268 365.25128l0 18.519684" fill-rule="evenodd"/><path fill="#a4c2f4" d="m205.13545 82.27259l314.42523 0c3.2869873 0 6.439392 1.3057632 8.763672 3.6300278c2.3242188 2.3242722 3.630005 5.4766617 3.630005 8.763672l0 12.3937c0 0.0015640259 -0.0012817383 0.0028305054 -0.0028076172 0.0028305054l-339.20978 -0.0028305054l0 0c-0.0015563965 0 -0.002822876 -0.0012664795 -0.002822876 -0.002822876l0.002822876 -12.390877l0 0c0 -6.8448486 5.5488586 -12.3937 12.393707 -12.3937z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m205.13545 82.27259l314.42523 0c3.2869873 0 6.439392 1.3057632 8.763672 3.6300278c2.3242188 2.3242722 3.630005 5.4766617 3.630005 8.763672l0 12.3937c0 0.0015640259 -0.0012817383 0.0028305054 -0.0028076172 0.0028305054l-339.20978 -0.0028305054l0 0c-0.0015563965 0 -0.002822876 -0.0012664795 -0.002822876 -0.002822876l0.002822876 -12.390877l0 0c0 -6.8448486 5.5488586 -12.3937 12.393707 -12.3937z" fill-rule="evenodd"/><path fill="#000000" d="m339.0376 99.98258l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.213104 5.5l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.619873 3.796875l0 -6.59375l-1.140625 0l0 -1.0l1.140625 0l0 -0.8125q0 -0.765625 0.125 -1.140625q0.1875 -0.5 0.65625 -0.8125q0.46875 -0.3125 1.3125 -0.3125q0.546875 0 1.203125 0.125l-0.1875 1.125q-0.40625 -0.0625 -0.765625 -0.0625q-0.578125 0 -0.828125 0.25q-0.234375 0.25 -0.234375 0.9375l0 0.703125l1.46875 0l0 1.0l-1.46875 0l0 6.59375l-1.28125 0zm3.7594604 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm3.2249146 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490509 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm6.666748 2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#6d9eeb" d="m17.29293 195.78148l502.26773 0c3.2869873 0 6.439392 1.3057556 8.763672 3.6300201c2.3242188 2.3242798 3.630005 5.4766693 3.630005 8.763672l0 12.393707c0 0.0024261475 -0.001953125 0.0043945312 -0.0043945312 0.0043945312l-527.0507 -0.0043945312l0 0c-0.0024256706 0 -0.004392147 -0.0019683838 -0.004392147 -0.0043945312l0.004392147 -12.389313l0 0c0 -6.8448486 5.5488486 -12.393692 12.393702 -12.393692z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m17.29293 195.78148l502.26773 0c3.2869873 0 6.439392 1.3057556 8.763672 3.6300201c2.3242188 2.3242798 3.630005 5.4766693 3.630005 8.763672l0 12.393707c0 0.0024261475 -0.001953125 0.0043945312 -0.0043945312 0.0043945312l-527.0507 -0.0043945312l0 0c-0.0024256706 0 -0.004392147 -0.0019683838 -0.004392147 -0.0043945312l0.004392147 -12.389313l0 0c0 -6.8448486 5.5488486 -12.393692 12.393702 -12.393692z" fill-rule="evenodd"/><path fill="#000000" d="m239.0048 213.49147l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.213104 5.5l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.3906097 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.5937347 0 -2.5781097 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.6093597 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96873474 0 -1.6093597 0.71875q-0.625 0.71875 -0.625 2.1875zm10.119858 2.640625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm0.77508545 -2.640625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm12.260498 1.015625l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm1.890625 -1.015625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2760925 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm2.771759 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/></g></svg>
\ No newline at end of file
diff --git a/docs/mkdocs/src/images/console_screenshot.png b/docs/mkdocs/src/images/console_screenshot.png
new file mode 100644
index 0000000..a9f45a1
--- /dev/null
+++ b/docs/mkdocs/src/images/console_screenshot.png
Binary files differ
diff --git a/docs/mkdocs/src/images/favicon.ico b/docs/mkdocs/src/images/favicon.ico
new file mode 100644
index 0000000..e830561
--- /dev/null
+++ b/docs/mkdocs/src/images/favicon.ico
Binary files differ
diff --git a/docs/mkdocs/src/images/logo.png b/docs/mkdocs/src/images/logo.png
new file mode 100644
index 0000000..665d878
--- /dev/null
+++ b/docs/mkdocs/src/images/logo.png
Binary files differ
diff --git a/docs/mkdocs/src/images/logo_framed.png b/docs/mkdocs/src/images/logo_framed.png
new file mode 100644
index 0000000..d6704da
--- /dev/null
+++ b/docs/mkdocs/src/images/logo_framed.png
Binary files differ
diff --git a/docs/mkdocs/src/index.md b/docs/mkdocs/src/index.md
new file mode 100644
index 0000000..a6bf2ae
--- /dev/null
+++ b/docs/mkdocs/src/index.md
@@ -0,0 +1,166 @@
+Bumble, a Python Bluetooth Stack
+================================
+
+![logo](images/logo_framed.png){ width=100 height=100 }
+
+A Bluetooth stack, written in Python, useful for emulation, test, experimentation, and implementation of any sort of virtual device, with virtual or physical Bluetooth controllers.
+The project initially only supported BLE (Bluetooth Low Energy), but support for Bluetooth Classic was
+eventually added. Support for BLE is therefore currently somewhat more advanced than for Classic.
+
+!!! warning
+    This project is still very much experimental and in an alpha state where a lot of things are still missing or broken, and what's there changes frequently.
+    Also, there are still a few hardcoded values/parameters in some of the examples and apps which need to be changed (those will eventually be command line arguments, as appropriate)
+
+Overview
+--------
+
+The goal of this project is to offer a suite of components that can be put together to implement a number of tasks related to Bluetooth. That's fairly open-ended, but at the very least, it should be possible to:
+
+* Implement a virtual controller that can be attached to any compliant Bluetooth host that supports HCI.
+* Implement a Bluetooth host that communicates through a controller over HCI, including of course a virtual controller
+* Connect two or more virtual controllers together inside a single app, or across multiple apps over a network connection or local IPC
+* Scan, advertise, connect, pair
+* Implement a GATT client and server
+* Implement an SDP client and server
+* Create an L2CAP channel between two peers
+
+Some of the configurations that may be useful:
+
+* A virtual controller used with an emulated or simulated device
+* A GATT client and/or GATT server test application that can be connected to a real or virtual Bluetooth device
+* Simulate certain conditions, including errors, with precise control that normal Bluetooth stacks don't offer through their standard APIs
+
+See the [use cases page](use_cases/index.md) for more use cases.
+
+The project is implemented in Python (Python >= 3.8 is required). A number of APIs for functionality that is inherently I/O bound is implemented in terms of python coroutines with async IO. This means that all of the concurrent tasks run in the same thread, which makes everything much simpler and more predictable.
+
+![layers](images/bumble_layers.svg)
+
+What's Included
+---------------
+
+Components of a Bluetooth stack:
+
+## Controller
+The (virtual) Controller component exposes an HCI interface to a host, and connects to a virtual link-layer bus. Several instances of this class can be connected to the same bus, in which case they can communicate with each other (both broadcast for advertising data and unicast for ACL data). The bus may be
+process-local, in which case all the controllers attached to the bus run in the same process, or
+it may be remote (see Remote Link), in which case several controllers in separate processes can
+communicate with each other.
+
+### Link
+The Controller component communicates with other virtual controllers through a Link interface.
+The link interface defines basic functionality like connection, disconnection, sending and
+receiving ACL data, sending and receiving advertising data, and more.
+Included in the project are two types of Link interface implementations:
+
+#### Local Link
+The LocalLink implementation is a simple object used by an application that instantiates
+more than one Controller objects and connects them in-memory and in-process.
+
+#### Remote Link
+The RemoteLink implementation communicates with other virtual controllers over a WebSocket.
+Multiple instances of RemoteLink objects communicate with each other through a simple
+WebSocket relay that can host any number of virtual 'rooms', where each 'room' is
+a set of controllers that can communicate between themselves.
+The `link_relay` app is where this relay is implemented.
+
+## Host
+The Host component connects to a controller over an HCI interface. It is responsible to sending commands and ACL data to the controller and receiving back events and ACL data.
+
+## Channel Manager
+The ChannelManager is responsible for managing L2CAP channels.
+
+## Security Manager
+The SecurityManager is responsible for pairing/bonding.
+
+## GATT Client
+The GATT Client offers an API to discover peer services and characteristics, reading and writing characteristics, subscribing to characteristics, and all other GATT client functions.
+
+## GATT Server
+The GATT Server offers an API to expose services and characteristics, responding to reads and writes on characteristics, handling subscriptions to characteristics, and all other GATT server functions.
+
+## SDP
+SDP implements the service discovery protocol for Bluetooth Classic.
+
+## RFComm
+RFComm is a bi-directional serial-port-like protocol. It is used in several profiles.
+
+## Device
+The Device component it a compound object that ties together a Host, GATT Client, GATT Server, L2CAP channel access, advertising and scanning, and more.
+
+## Profiles
+Profiles are ways of using the underlying protocols for certain well-defined used cases, like playing music, implementing a headset, and so on.
+
+### A2DP
+A2DP is the Advanced Audio Profile, which enables asynchronous streaming of audio to speakers, or from microphones. Both the "source" (typically music playback source) and "sink" (typically a speaker) functions of A2DP.
+
+### HFP
+Hands Free Profile. Used for headsets.
+
+### HID
+Human Interface Device. For keyboards, mice, etc.
+
+## Transports
+The Hosts and Controllers communicate over a transport, which is responsible for sending/receiving
+HCI packets.
+Several types of transports are supported:
+
+  * **In Process**: HCI packets are passed via a function call
+  * Serial: interface with a controller over a serial port (HCI UART, like a development board or serial Bluetooth dongle)
+  * **USB**: interface with a controller over USB (HCI USB, like a Bluetooth USB dongle)
+  * **UDP**: packets are sent to a specified host/port and received on a specified port over a UDP socket
+  * **TCP Client**: a connection to a TCP server is made, after which HCI packets are sent/received over a TCP socket
+  * **TCP Server**: listens for a TCP client on a specified port. When a client connection is made, HCI packets are sent/received over a TCP socket
+  * **WebSocket Client**: a connection to a WebSocket server is made, after which HCI packets are sent/received over the socket.
+  * **WebSocket Server**: listens for a WebSocket client on a specified port. When a client connection is made, HCI packets are sent/received over the socket.
+  * **PTY**: a PTY (pseudo terminal) is used to send/receive HCI packets. This is convenient to expose a virtual controller as if it were an HCI UART
+  * **VHCI**: used to attach a virtual controller to a Bluetooth stack on platforms that support it.
+  * **HCI** Socket: an HCI socket, on platforms that support it, to send/receive HCI packets to/from an HCI controller managed by the OS.
+  * **Android Emulator**: a gRPC connection to an Android emulator is used to setup either an HCI interface to the emulator's "Root Canal" virtual controller, or attach a virtual controller to the Android Bluetooth host stack.
+  * **File**: HCI packets are read/written to a file-like node in the filesystem.
+
+A Bumble Host object communicates with a Bumble Controller object, or external Controller, via a Transport connection. A Bumble Controller object communicates with a Bumble Host, or external Host, via a Transport connection. When both the Host and Controller are Bumble objects, they typically communicate In Process, or via a Link Relay.
+
+See the [Transports page](transports/index.md) for details.
+
+Hardware
+--------
+The Host part of the stack can interact with Bumble's Controller implementation, but also with external hardware controllers.
+
+See the [Hardware page](hardware/index.md) for details.
+
+
+Examples
+--------
+
+See the [Examples page](examples/index.md)
+
+Apps & Tools
+------------
+
+See the [Apps & Tools page](apps_and_tools/index.md)
+
+Platforms
+---------
+
+The core library should work on any platform on which you can run Python3.
+Some platforms support features that not all platforms support
+
+  * :material-apple: macOS - see the [macOS platform page](platforms/macos.md)
+  * :material-linux: Linux - see the [Linux platform page](platforms/linux.md)
+  * :material-microsoft-windows: Windows - see the [Windows platform page](platforms/windows.md)
+  * :material-android: Android - see the [Android platform page](platforms/android.md)
+
+See the [Platforms page](platforms/index.md) for details.
+
+Roadmap
+-------
+
+Future features to be considered include:
+
+  * More device examples
+  * Add a new type of virtual link (beyond the two existing ones) to allow for link-level simulation (timing, loss, etc)
+  * Bindings for languages other than Python
+  * RPC interface to expose most of the API for remote use
+  * (...suggest anything you want...)
+
diff --git a/docs/mkdocs/src/platforms/android.md b/docs/mkdocs/src/platforms/android.md
new file mode 100644
index 0000000..872edc7
--- /dev/null
+++ b/docs/mkdocs/src/platforms/android.md
@@ -0,0 +1,89 @@
+:material-android: ANDROID PLATFORM
+===================================
+
+Using Bumble with Android is not about running the Bumble stack on the Android
+OS itself, but rather using Bumble with the Bluetooth support of the Android 
+emulator.
+
+The two main use cases are:
+
+  * Connecting the Bumble host stack to the Android emulator's virtual controller.
+  * Using Bumble as an HCI bridge to connect the Android emulator to a physical 
+    Bluetooth controller, such as a USB dongle
+
+!!! warning
+    Bluetooth support in the Android emulator is a recent feature that may still
+    be evolving. The information contained here be somewhat out of sync with the
+    version of the emulator you are using.
+    You will need version 31.3.8.0 or later.
+
+The Android emulator supports Bluetooth in two ways: either by exposing virtual 
+Bluetooth controllers to which you can connect a virtual Bluetooth host stack, or
+by exposing an way to connect your own virtual controller to the Android Bluetooth
+stack via a virtual HCI interface.
+Both ways are controlled via gRPC requests to the Android emulator.
+
+## Launching the Emulator
+
+If the version of the emulator you are running does not yet support enabling 
+Bluetooth support by default or automatically, you must launch the emulator from
+the command line.
+
+!!! tip
+    For details on how to launch the Android emulator from the command line,
+    visit [this Android Studio user guide page](https://developer.android.com/studio/run/emulator-commandline)
+
+The `-grpc <port>` command line option may be used to select a gRPC port other than the default.
+
+## Connecting to Root Canal
+
+The Android emulator's virtual Bluetooth controller is called **Root Canal**.
+Multiple instances of Root Canal virtual controllers can be instantiated, they
+communicate link layer packets between them, thus creating a virtual radio network.
+Configuring a Bumble Device instance to use Root Canal as a virtual controller
+allows that virtual device to communicate with the Android Bluetooth stack, and
+through it with Android applications as well as system-managed profiles.
+To connect a Bumble host stack to a Root Canal virtual controller instance, use 
+the bumble `android-emulator` transport in `host` mode (the default).
+
+!!! example "Run the example GATT server connected to the emulator"
+    ``` shell
+    $ python run_gatt_server.py device1.json android-emulator
+    ```
+
+## Connecting a Custom Virtual Controller
+
+This is an advanced use case, which may not be officially supported, but should work in recent
+versions of the emulator.
+You will likely need to start the emulator from the command line, in order to specify the `-forward-vhci` option (unless the emulator offers a way to control that feature from a user/ui menu).
+
+!!! example "Launch the emulator with VHCI forwarding"
+    In this example, we launch an emulator AVD named "Tiramisu"
+    ```shell
+    $ emulator -forward-vhci -avd Tiramisu
+    ```
+
+!!! tip
+    Attaching a virtual controller use the VHCI forwarder while the Android Bluetooth stack
+    is running isn't supported. So you need to disable Bluetooth in your running Android guest
+    before attaching the virtual controller, then re-enable it once attached.
+
+To connect a virtual controller to the Android Bluetooth stack, use the bumble `android-emulator` transport in `controller` mode. For example, using the default gRPC port, the transport name would be: `android-emulator:mode=controller`.
+
+!!! example "Connect the Android emulator to the first USB Bluetooth dongle, using the `hci_bridge` application"
+    ```shell
+    $ bumble-hci-bridge android-emulator:mode=controller usb:0
+    ```
+
+## Other Tools
+
+The `show` application that's included with Bumble can be used to parse and pretty-print the HCI packets
+from an Android HCI "snoop log" (see [this page](https://source.android.com/devices/bluetooth/verifying_debugging) 
+for details on how to obtain HCI snoop logs from an Android device).
+Use the `--format snoop` option to specify that the file is in that specific format.
+
+!!! example "Analyze an Android HCI snoop log file"
+    ```shell
+    $ bumble-show --format snoop btsnoop_hci.log
+    ```
+
diff --git a/docs/mkdocs/src/platforms/index.md b/docs/mkdocs/src/platforms/index.md
new file mode 100644
index 0000000..a93e947
--- /dev/null
+++ b/docs/mkdocs/src/platforms/index.md
@@ -0,0 +1,11 @@
+PLATFORMS
+=========
+
+Most of the code included in the project should run on any platform that supports Python >= 3.8. Not all features are supported on all platforms (for example, USB dongle support is only available on platforms where the python USB library is functional).
+
+For platform-specific information, see the following pages:
+
+  * :material-apple: macOS - see the [macOS platform page](macos.md)
+  * :material-linux: Linux - see the [Linux platform page](linux.md)
+  * :material-microsoft-windows: Windows - see the [Windows platform page](windows.md)
+  * :material-android: Android - see the [Android platform page](android.md)
diff --git a/docs/mkdocs/src/platforms/linux.md b/docs/mkdocs/src/platforms/linux.md
new file mode 100644
index 0000000..6468cd9
--- /dev/null
+++ b/docs/mkdocs/src/platforms/linux.md
@@ -0,0 +1,138 @@
+:material-linux: LINUX PLATFORM
+===============================
+
+In addition to all the standard functionality available from the project by running the python tools and/or writing your own apps by leveraging the API, it is also possible on Linux hosts to interface the Bumble stack with the native BlueZ stack, and with Bluetooth controllers.
+
+Using Bumble With BlueZ
+-----------------------
+
+A Bumble virtual controller can be attached to the BlueZ stack.
+Attaching a controller to BlueZ can be done by either simulating a UART HCI interface, or by using the VHCI driver interface if available.
+In both cases, the controller can run locally on the Linux host, or remotely on a different host, with a bridge between the remote controller and the local BlueZ host, which may be useful when the BlueZ stack is running on an embedded system, or a host on which running the Bumble controller is not convenient.
+
+### Using VHCI
+
+With the [VHCI transport](../transports/vhci.md) you can attach a Bumble virtual controller to the BlueZ stack. Once attached, the controller will appear just like any other controller, and thus can be used with the standard BlueZ tools.
+
+!!! example "Attaching a virtual controller"
+    With the example app `run_controller.py`:
+    ```
+    PYTHONPATH=. python3 examples/run_controller.py F6:F7:F8:F9:FA:FB examples/device1.json vhci
+    ```
+    
+    You should see a 'Virtual Bus' controller. For example:
+    ```
+    $ hciconfig
+    hci0:	Type: Primary  Bus: Virtual
+        BD Address: F6:F7:F8:F9:FA:FB  ACL MTU: 27:64  SCO MTU: 0:0
+        UP RUNNING 
+        RX bytes:0 acl:0 sco:0 events:43 errors:0
+        TX bytes:274 acl:0 sco:0 commands:43 errors:0
+    ```
+
+    And scanning for devices should show the virtual 'Bumble' device that's running as part of the `run_controller.py` example app:
+    ```
+    pi@raspberrypi:~ $ sudo hcitool -i hci2 lescan
+    LE Scan ...
+    F0:F1:F2:F3:F4:F5 Bumble
+    ```
+
+### Using HCI Sockets
+
+HCI sockets provide a way to send/receive HCI packets to/from a Bluetooth controller managed by the kernel.
+The HCI device referenced by an `hci-socket` transport (`hciX`, where `X` is an integer, with `hci0` being the first controller device, and so on) must be in the `DOWN` state before it can be opened as a transport.
+You can bring a HCI controller `UP` or `DOWN` with `hciconfig`.
+
+!!! tip "List all available controllers"
+    The command
+    ```
+    $ hciconfig
+    ```
+    lists all available HCI controllers and their state.
+
+    Example:
+
+    ```
+    pi@raspberrypi:~ $ hciconfig
+    hci1:	Type: Primary  Bus: USB
+        BD Address: 00:16:A4:5A:40:F2  ACL MTU: 1021:8  SCO MTU: 64:1
+        DOWN 
+        RX bytes:84056 acl:0 sco:0 events:51 errors:0
+        TX bytes:1980 acl:0 sco:0 commands:90 errors:0
+
+    hci0:	Type: Primary  Bus: UART
+        BD Address: DC:A6:32:75:2C:97  ACL MTU: 1021:8  SCO MTU: 64:1
+        DOWN 
+        RX bytes:68038 acl:0 sco:0 events:692 errors:0
+        TX bytes:20105 acl:0 sco:0 commands:843 errors:0
+    ```
+
+!!! tip "Disabling `bluetoothd`"
+    When the Bluetooth daemon, `bluetoothd`, is running, it will try to use any HCI controller attached to the BlueZ stack, automatically. This means that whenever an HCI socket transport is released, it is likely that `bluetoothd` will take it over, so you will get a "device busy" condition (ex: `OSError: [Errno 16] Device or resource busy`). If that happens, you can always use 
+    ```
+    $ hciconfig hci0 down
+    ``` 
+    (or `hciX` with `X` being the index of the controller device you want to use), but a simpler solution is to just stop the `bluetoothd` daemon, with a command like:
+    ```
+    $ sudo systemctl stop bluetooth.service
+    ```
+    You can always re-start the daemon with
+    ```
+    $ sudo systemctl start bluetooth.service
+    ```
+
+### Using a Simulated UART HCI
+
+### Bridge to a Remote Controller
+
+
+Using Bumble With Bluetooth Controllers
+---------------------------------------
+
+A Bumble application can interface with a local Bluetooth controller.
+If your Bluetooth controller is a standard HCI USB controller, see the [USB Transport page](../transports/usb.md) for details on how to use HCI USB controllers.
+If your Bluetooth controller is a standard HCI UART controller, see the [Serial Transport page](../transports/serial.md).
+Alternatively, a Bumble Host object can communicate with one of the platform's controllers via an HCI Socket.
+
+`<details to be filled in>`
+
+### Raspberry Pi 4 :fontawesome-brands-raspberry-pi:
+
+You can use the Bluetooth controller either via the kernel, or directly to the device.
+
+#### Via The Kernel
+
+Use an HCI Socket transport
+
+#### Directly
+In order to use the Bluetooth controller directly on a Raspberry Pi 4 board, you need to ensure that it isn't being used by the BlueZ stack (which it probably is by default).
+
+```
+$ sudo systemctl stop hciuart
+```
+should detach the controller from the stack, after which you can use the HCI UART with Bumble.
+
+!!! tip "Check the device name for the UART and at what speed it should be opened"
+    ```
+    $ sudo systemctl status hciuart
+    ```
+    should show the speed at which the UART should be opened.
+    For example:
+    ```
+    $ sudo systemctl status hciuart
+     hciuart.service - Configure Bluetooth Modems connected by UART
+      Loaded: loaded (/lib/systemd/system/hciuart.service; enabled; vendor preset: enabled)
+      Active: active (running) since Fri 2021-06-18 02:17:28 BST; 1min 10s ago
+     Process: 357 ExecStart=/usr/bin/btuart (code=exited, status=0/SUCCESS)
+    Main PID: 586 (hciattach)
+       Tasks: 1 (limit: 4915)
+      CGroup: /system.slice/hciuart.service
+              └─586 /usr/bin/hciattach /dev/serial1 bcm43xx 3000000 flow -
+    ```
+    When run before stopping the `hciuart` service, shows that on this board, the UART device is `/dev/serial` and the speed is `3000000`
+
+!!! example "Example: scanning"
+    ```
+    python3 run_scanner.py serial:/dev/serial1,3000000
+    ```
+
diff --git a/docs/mkdocs/src/platforms/macos.md b/docs/mkdocs/src/platforms/macos.md
new file mode 100644
index 0000000..7c5bc32
--- /dev/null
+++ b/docs/mkdocs/src/platforms/macos.md
@@ -0,0 +1,14 @@
+:material-apple: MACOS PLATFORM
+===============================
+
+USB HCI
+-------
+
+To use the experimental USB HCI support on macOS, you need to tell macOS not to use the USB Bluetooth
+controller with its internal Bluetooth stack.
+To do that, use the following command:
+```
+sudo nvram bluetoothHostControllerSwitchBehavior="never"
+```
+A reboot shouldn't be necessary after that. See [Tech Note 2295](https://developer.apple.com/library/archive/technotes/tn2295/_index.html)
+
diff --git a/docs/mkdocs/src/platforms/windows.md b/docs/mkdocs/src/platforms/windows.md
new file mode 100644
index 0000000..2157695
--- /dev/null
+++ b/docs/mkdocs/src/platforms/windows.md
@@ -0,0 +1,13 @@
+:material-microsoft-windows: WINDOWS PLATFORM
+=============================================
+
+USB HCI
+-------
+
+To use a Bluetooth USB dongle on Windows, you need a USB dongle that does not require a vendor Windows driver (the dongle will be used directly through the [`WinUSB`](https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb) driver rather than through a vendor-supplied Windows driver).
+
+In order to use the dongle, the `WinUSB` driver must be assigned to the USB device. It is likely that, by default, when you first plug in the dongle, it will be recognized by Windows as a Bluetooth USB device, and Windows will try to use it with its native Bluetooth stack. You will need to switch the driver, which can be done easily with the [Zadig tool](https://zadig.akeo.ie/).
+In the Zadig tool, select your USB dongle device, and associate it with WinUSB.  
+Once the WinUSB driver is correctly assigned to your device, you can confirm that by checking the settings with the Windows Device Manager control panel. Your device should appear under "Universal Serial Bus Device" (not under "Bluetooth"), and inspecting the driver details, you should see `winusb.sys` in the list of driver files.
+
+![USB Driver Details](winusb_driver.png)
diff --git a/docs/mkdocs/src/platforms/winusb_driver.png b/docs/mkdocs/src/platforms/winusb_driver.png
new file mode 100755
index 0000000..103384f
--- /dev/null
+++ b/docs/mkdocs/src/platforms/winusb_driver.png
Binary files differ
diff --git a/docs/mkdocs/src/transports/android_emulator.md b/docs/mkdocs/src/transports/android_emulator.md
new file mode 100644
index 0000000..d903394
--- /dev/null
+++ b/docs/mkdocs/src/transports/android_emulator.md
@@ -0,0 +1,21 @@
+ANDROID EMULATOR TRANSPORT
+==========================
+
+The Android emulator transport either connects, as a host, to a "Root Canal" virtual controller
+("host" mode), or attaches a virtual controller to the Android Bluetooth host stack ("controller" mode).
+
+## Moniker
+The moniker syntax for an Android Emulator transport is: `android-emulator:[mode=<host|controller>][mode=<host|controller>]`.
+Both the `mode=<host|controller>` and `mode=<host|controller>` parameters are optional (so the moniker `android-emulator` by itself is a valid moniker, which will create a transport in `host` mode, connected to `localhost` on the default gRPC port for the emulator)
+
+!!! example Example
+    `android-emulator`  
+    connect as a host to the emulator on localhost:8554
+
+!!! example Example
+    `android-emulator:mode=controller`  
+    connect as a controller to the emulator on localhost:8554
+
+!!! example Example
+    `android-emulator:localhost:8555`  
+    connect as a host to the emulator on localhost:8555
diff --git a/docs/mkdocs/src/transports/file.md b/docs/mkdocs/src/transports/file.md
new file mode 100644
index 0000000..d20154f
--- /dev/null
+++ b/docs/mkdocs/src/transports/file.md
@@ -0,0 +1,12 @@
+FILE TRANSPORT
+==============
+
+The File transport allows opening any named entry on a filesystem and use it for HCI transport I/O.
+This is typically used to open a PTY, or unix driver, not for real files.
+
+## Moniker
+The moniker for a File transport is `file:<path>`
+
+!!! example
+    `file:/dev/ttys001`  
+    Opens the pseudo terminal `/dev/ttys001` as a transport
\ No newline at end of file
diff --git a/docs/mkdocs/src/transports/hci_socket.md b/docs/mkdocs/src/transports/hci_socket.md
new file mode 100644
index 0000000..58cdbfb
--- /dev/null
+++ b/docs/mkdocs/src/transports/hci_socket.md
@@ -0,0 +1,17 @@
+HCI SOCKET TRANSPORT
+====================
+
+An HCI Socket can send/receive HCI packets to/from a Bluetooth HCI controller managed by the host OS. This is only supported on some platforms (currently only tested on Linux).
+
+!!! note
+    This type of transport can only be used for virtual hosts, not virtual controllers
+
+## Moniker
+The moniker for an HCI Socket transport is either just `hci-socket` (to use the default/first Bluetooth controller), or `hci-socket:<index>` where `<index>` is the 0-based index of a Bluetooth controller device.
+
+!!! example
+    `hci-socket`  
+    Use an HCI socket to the first Bluetooth controller (`hci0 on Linux`)
+
+!!! tip "On Linux"
+    See the [Linux Platform](../platforms/linux.md) page for details on how to use HCI sockets on Linux
\ No newline at end of file
diff --git a/docs/mkdocs/src/transports/index.md b/docs/mkdocs/src/transports/index.md
new file mode 100644
index 0000000..70827c1
--- /dev/null
+++ b/docs/mkdocs/src/transports/index.md
@@ -0,0 +1,20 @@
+TRANSPORTS
+==========
+
+The Hosts and Controllers communicate over a transport, which is responsible for sending/receiving
+HCI packets.
+Several types of transports are supported:
+
+  * In Process: HCI packets are passed via a function call
+  * [Serial](serial.md): interface with a controller over a serial port (HCI UART, like a development board or serial Bluetooth dongle)
+  * [USB](usb.md): interface with a controller over USB (HCI USB, like a Bluetooth USB dongle)
+  * [UDP](udp.md): packets are sent to a specified host/port and received on a specified port over a UDP socket
+  * [TCP Client](tcp_client.md): a connection to a TCP server is made, after which HCI packets are sent/received over a TCP socket
+  * [TCP Server](tcp_server.md): listens for a TCP client on a specified port. When a client connection is made, HCI packets are sent/received over a TCP socket
+  * [WebSocket Client](ws_client.md): a connection to a WebSocket server is made, after which HCI packets are sent/received over the socket.
+  * [WebSocket Server](ws_server.md): listens for a WebSocket client on a specified port. When a client connection is made, HCI packets are sent/received over the socket.
+  * [PTY](pty.md): a PTY (pseudo terminal) is used to send/receive HCI packets. This is convenient to expose a virtual controller as if it were an HCI UART
+  * [VHCI](vhci.md): used to attach a virtual controller to a Bluetooth stack on platforms that support it.
+  * [HCI Socket](hci_socket.md): an HCI socket, on platforms that support it, to send/receive HCI packets to/from an HCI controller managed by the OS.
+  * [Android Emulator](android_emulator.md): a gRPC connection to an Android emulator is used to setup either an HCI interface to the emulator's "Root Canal" virtual controller, or attach a virtual controller to the Android Bluetooth host stack.
+  * [File](file.md): HCI packets are read/written to a file-like node in the filesystem.
diff --git a/docs/mkdocs/src/transports/pty.md b/docs/mkdocs/src/transports/pty.md
new file mode 100644
index 0000000..fafa3c9
--- /dev/null
+++ b/docs/mkdocs/src/transports/pty.md
@@ -0,0 +1,12 @@
+PTY TRANSPORT
+=============
+
+The PTY transport uses a Unix pseudo-terminal device to communicate with another process on the host, as if it were over a serial port.
+
+## Moniker
+The moniker syntax for a PTY transport is: `pty[:path]`.
+Where `path`, is used, is the path name where a symbolic link to the PTY will be created for convenience (the link will be removed when the transport is closed or when the process exits).
+
+!!! example
+    `pty:virtual_hci`  
+    Creates a PTY entry and a symbolic link, named `virtual_hci`, linking to the PTY
diff --git a/docs/mkdocs/src/transports/serial.md b/docs/mkdocs/src/transports/serial.md
new file mode 100644
index 0000000..3ce2e38
--- /dev/null
+++ b/docs/mkdocs/src/transports/serial.md
@@ -0,0 +1,12 @@
+SERIAL TRANSPORT
+================
+
+The serial transport implements sending/receiving HCI packets over a UART (a.k.a serial port).
+
+## Moniker
+The moniker syntax for a serial transport is: `serial:<device-path>[,<speed>]`
+When `<speed>` is omitted, the default value of 1000000 is used
+
+!!! example
+    `serial:/dev/tty.usbmodem0006839912172,1000000`  
+    Opens the serial port `/dev/tty.usbmodem0006839912172` at `1000000`bps
diff --git a/docs/mkdocs/src/transports/tcp_client.md b/docs/mkdocs/src/transports/tcp_client.md
new file mode 100644
index 0000000..39fb477
--- /dev/null
+++ b/docs/mkdocs/src/transports/tcp_client.md
@@ -0,0 +1,11 @@
+TCP CLIENT TRANSPORT
+====================
+
+The TCP Client transport uses an outgoing TCP connection to a host:port address.
+
+## Moniker
+The moniker syntax for a TCP client transport is: `tcp-client:<remote-host>:<remote-port>`
+
+!!! example
+    `tcp-client:127.0.0.1:9001`  
+    Connects to port 9001 on the local host
\ No newline at end of file
diff --git a/docs/mkdocs/src/transports/tcp_server.md b/docs/mkdocs/src/transports/tcp_server.md
new file mode 100644
index 0000000..3af8ff0
--- /dev/null
+++ b/docs/mkdocs/src/transports/tcp_server.md
@@ -0,0 +1,13 @@
+TCP SERVER TRANSPORT
+====================
+
+The TCP Client transport uses an incoming TCP connection to a host:port address.
+
+## Moniker
+The moniker syntax for a TCP server transport is: `tcp-server:<local-host>:<local-port>`
+where `<local-host>` may be the address of a local network interface, or `_` to accept 
+connections on all local network interfaces.
+
+!!! example
+    `tcp-server:_:9001`  
+    Waits for and accepts connections on port `9001`
\ No newline at end of file
diff --git a/docs/mkdocs/src/transports/udp.md b/docs/mkdocs/src/transports/udp.md
new file mode 100644
index 0000000..4238bc8
--- /dev/null
+++ b/docs/mkdocs/src/transports/udp.md
@@ -0,0 +1,11 @@
+UDP TRANSPORT
+=============
+
+The UDP transport is a UDP socket, receiving packets on a specified port number, and sending packets to a specified host and port number.
+
+## Moniker
+The moniker syntax for a UDP transport is: `udp:<local-host>:<local-port>,<remote-host>:<remote-port>`.
+
+!!! example
+    `udp:0.0.0.0:9000,127.0.0.1:9001`  
+    UDP transport where packets are received on port `9000` and sent to `127.0.0.1` on port `9001`
diff --git a/docs/mkdocs/src/transports/usb.md b/docs/mkdocs/src/transports/usb.md
new file mode 100644
index 0000000..f35259b
--- /dev/null
+++ b/docs/mkdocs/src/transports/usb.md
@@ -0,0 +1,19 @@
+USB TRANSPORT
+=============
+
+The USB transport interfaces with a local Bluetooth USB dongle.
+
+## Moniker
+The moniker for a USB transport is either `usb:<index>` or `usb:<vendor>:<product>`
+with `<index>` as the 0-based index to select amongst all the devices that appear to be supporting Bluetooth HCI (0 being the first one), or where `<vendor>` and `<product>` are a vendor ID and product ID in hexadecimal.
+
+!!! example
+    `usb:04b4:f901`  
+    Use the USB dongle with `vendor` equal to `04b4` and `product` equal to `f901`
+
+    `usb:0`  
+    Use the first Bluetooth dongle
+
+## Alternative
+The library includes two different implementations of the USB transport, implemented using different python bindings for `libusb`.
+Using the transport prefix `pyusb:` instead of `usb:` selects the implementation based on  [PyUSB](https://pypi.org/project/pyusb/), using the synchronous API of `libusb`, whereas the default implementation is based on [libusb1](https://pypi.org/project/libusb1/), using the asynchronous API of `libusb`. In order to use the alternative PyUSB-based implementation, you need to ensure that you have installed that python module, as it isn't installed by default as a dependency of Bumble.
diff --git a/docs/mkdocs/src/transports/vhci.md b/docs/mkdocs/src/transports/vhci.md
new file mode 100644
index 0000000..9d967c4
--- /dev/null
+++ b/docs/mkdocs/src/transports/vhci.md
@@ -0,0 +1,14 @@
+VHCI TRANSPORT
+==============
+
+The VHCI transport allows attaching a virtual controller to the Bluetooth stack on operating systems that offer a VHCI driver (Linux, if enabled, maybe others).
+
+!!! note
+    This type of transport can only be used for virtual controllers, not virtual hosts
+
+## Moniker
+The moniker for a VHCI transport is either just `vhci` (to use the default VHCI device path at `/dev/vhci`), or `vhci:<path>` where `<path>` is the path of a VHCI device.
+
+!!! example
+    `vhci`  
+    Attaches a virtual controller transport to `/dev/vhci`
\ No newline at end of file
diff --git a/docs/mkdocs/src/transports/ws_client.md b/docs/mkdocs/src/transports/ws_client.md
new file mode 100644
index 0000000..4238bc8
--- /dev/null
+++ b/docs/mkdocs/src/transports/ws_client.md
@@ -0,0 +1,11 @@
+UDP TRANSPORT
+=============
+
+The UDP transport is a UDP socket, receiving packets on a specified port number, and sending packets to a specified host and port number.
+
+## Moniker
+The moniker syntax for a UDP transport is: `udp:<local-host>:<local-port>,<remote-host>:<remote-port>`.
+
+!!! example
+    `udp:0.0.0.0:9000,127.0.0.1:9001`  
+    UDP transport where packets are received on port `9000` and sent to `127.0.0.1` on port `9001`
diff --git a/docs/mkdocs/src/transports/ws_server.md b/docs/mkdocs/src/transports/ws_server.md
new file mode 100644
index 0000000..4238bc8
--- /dev/null
+++ b/docs/mkdocs/src/transports/ws_server.md
@@ -0,0 +1,11 @@
+UDP TRANSPORT
+=============
+
+The UDP transport is a UDP socket, receiving packets on a specified port number, and sending packets to a specified host and port number.
+
+## Moniker
+The moniker syntax for a UDP transport is: `udp:<local-host>:<local-port>,<remote-host>:<remote-port>`.
+
+!!! example
+    `udp:0.0.0.0:9000,127.0.0.1:9001`  
+    UDP transport where packets are received on port `9000` and sent to `127.0.0.1` on port `9001`
diff --git a/docs/mkdocs/src/use_cases/index.md b/docs/mkdocs/src/use_cases/index.md
new file mode 100644
index 0000000..d2fb45c
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/index.md
@@ -0,0 +1,11 @@
+USE CASES
+=========
+
+While the Bumble project aims to be a general purpose set of libraries and tools for any purpose, there are some typical use case patterns that are typical and representative of what can be done.
+
+  * [Use case 1](use_case_1.md) - Bumble python application connected to a "real" Bluetooth device with a physical controller
+  * [Use case 2](use_case_2.md) - Native Bluetooth application connected to a Bumble python application with virtual controller
+  * [Use case 3](use_case_3.md) - Emulated Bluetooth device connected to a Bumble python application with virtual controller
+  * [Use case 4](use_case_4.md) - Connecting two emulated Bluetooth devices
+  * [Use case 5](use_case_5.md) - Connecting multiple Bumble python applications
+  * [Use case 6](use_case_6.md) - Connecting an emulated Bluetooth device to a physical controller
diff --git a/docs/mkdocs/src/use_cases/use_case_1.md b/docs/mkdocs/src/use_cases/use_case_1.md
new file mode 100644
index 0000000..3b738ac
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/use_case_1.md
@@ -0,0 +1,14 @@
+USE CASE 1
+==========
+
+# Bumble python application connected to a device with a "real" Bluetooth controller
+
+Write a python application (ex: a GATT client that will connect to a hear rate sensor, or a GATT server exposing a battery level) that can connect to or receive connections from a "real" Bluetooth device (like a sensor, or a mobile phone) using a Bluetooth controller (a USB dongle, or HCI-UART controller)
+
+```
++--------++--------+             +------------+             +-----------+
+| Bumble || Bumble |             | Physical   |             | Bluetooth |
+| Python || Host   |<--  HCI  -->| Controller |{...radio...}| Device    |
+| App    ||        |  Transport  |            |             |           |
++--------++--------+             +------------+             +-----------+
+```
\ No newline at end of file
diff --git a/docs/mkdocs/src/use_cases/use_case_2.md b/docs/mkdocs/src/use_cases/use_case_2.md
new file mode 100644
index 0000000..6875a03
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/use_case_2.md
@@ -0,0 +1,14 @@
+USE CASE 2
+==========
+
+# Native Bluetooth application connected to a Bumble python application with virtual controller
+
+Connect a native Bluetooth application, running on a host with Bluetooth stack to which we can attach a virtual controller (Linux for example), to a Bumble python application (ex: a GATT server or client).
+
+```
++-----------+             +------------+                +------------++--------++--------+
+| Native    |             | Bumble     |    Bumble      | Bumble     || Bumble || Bumble |
+| Bluetooth |<--  HCI  -->| Virtual    |<== Local or ==>| Virtual    || Host   || Python |
+| App       |  Transport  | Controller |    Remote      | Controller ||        || App    |
++-----------+             +------------+    Link        +------------++--------++--------+
+```
\ No newline at end of file
diff --git a/docs/mkdocs/src/use_cases/use_case_3.md b/docs/mkdocs/src/use_cases/use_case_3.md
new file mode 100644
index 0000000..0cf477d
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/use_case_3.md
@@ -0,0 +1,14 @@
+USE CASE 3
+==========
+
+# Emulated Bluetooth device connected to a Bumble python application with virtual controller
+
+Connect an emulated Bluetooth device (ex: an Android emulator, or an embedded device emulator) to a Bumble python application (ex: a GATT server or client).
+
+```
++-----------+             +------------+                +------------++--------++--------+
+| Emulated  |             | Bumble     |    Bumble      | Bumble     || Bumble || Bumble |
+| Bluetooth |<--  HCI  -->| Virtual    |<== Local or ==>| Virtual    || Host   || Python |
+| Device    |  Transport  | Controller |    Remote      | Controller ||        || App    |
++-----------+             +------------+    Link        +------------++--------++--------+
+```
\ No newline at end of file
diff --git a/docs/mkdocs/src/use_cases/use_case_4.md b/docs/mkdocs/src/use_cases/use_case_4.md
new file mode 100644
index 0000000..762f974
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/use_case_4.md
@@ -0,0 +1,14 @@
+USE CASE 4
+==========
+
+# Connecting two emulated Bluetooth devices
+
+Connect two emulated Bluetooth device (ex: an Android emulator, or an embedded device emulator) to each other
+
+```
++-----------+             +------------+                +------------+             +-----------+ 
+| Emulated  |             | Bumble     |    Bumble      | Bumble     |             | Emulated  |
+| Bluetooth |<--  HCI  -->| Virtual    |<== Local or ==>| Virtual    |<--  HCI  -->| Bluetooth |
+| Device    |  Transport  | Controller |    Remote      | Controller |  Transport  | Device    |
++-----------+             +------------+    Link        +------------+             +-----------+
+```
\ No newline at end of file
diff --git a/docs/mkdocs/src/use_cases/use_case_5.md b/docs/mkdocs/src/use_cases/use_case_5.md
new file mode 100644
index 0000000..7232105
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/use_case_5.md
@@ -0,0 +1,20 @@
+USE CASE 5
+==========
+
+# Connecting multiple Bumble python applications
+
+Write several python applications (ex: a GATT client that will connect to a hear rate sensor, or a GATT server exposing a battery level) and connect them together
+
+```
++--------++--------++------------+                        +------------++--------++--------+
+| Bumble || Bumble || Bumble     |                        | Bumble     || Bumble || Bumble |
+| Python || Host   || Controller |<--+                +-->| Controller || Host   || Python |
+| App    ||        ||            |   |   +--------+   |   |            ||        || App    |
++--------++--------++------------+   +-->| Bumble |<--+   +------------++--------++--------+
+                                         | Link   |
++--------++--------++------------+   +-->| Relay  |<--+   +------------++--------++--------+
+| Bumble || Bumble || Bumble     |   |   +--------+   |   | Bumble     || Bumble || Bumble |
+| Python || Host   || Controller |<--+                +-->| Controller || Host   || Python |
+| App    ||        ||            |                        |            ||        || App    |
++--------++--------++------------+                        +------------++--------++--------+
+```
\ No newline at end of file
diff --git a/docs/mkdocs/src/use_cases/use_case_6.md b/docs/mkdocs/src/use_cases/use_case_6.md
new file mode 100644
index 0000000..f27a184
--- /dev/null
+++ b/docs/mkdocs/src/use_cases/use_case_6.md
@@ -0,0 +1,14 @@
+USE CASE 6
+==========
+
+# Connecting an emulated Bluetooth device to a physical controller
+
+It can be useful to connect an emulated device (like a phone simulator, or an emulated embedded device) to a physical controller in order to connect to other Bluetooth devices. By doing this via a Bumble HCI bridge, it becomes easy to inspect the HCI packets exchanged with the controller, and possibly filter/change them if needed (for example to support vendor-specific HCI extensions).
+
+```
++-----------+             +--------+             +------------+             +-----------+ 
+| Emulated  |             | Bumble |             | Physical   |             | Bluetooth |
+| Bluetooth |<--  HCI  -->| HCI    |<--  HCI  -->| Controller |{...radio...}| Device    |
+| Device    |  Transport  | Bridge |  Transport  |            |             |           |
++-----------+             +--------+             +------------+             +-----------+
+```
\ No newline at end of file
diff --git a/docs/mkdocs/theme/partials/footer.html b/docs/mkdocs/theme/partials/footer.html
new file mode 100644
index 0000000..1c55f92
--- /dev/null
+++ b/docs/mkdocs/theme/partials/footer.html
@@ -0,0 +1,54 @@
+{#-
+  This file was automatically generated - do not edit
+-#}
+{% import "partials/language.html" as lang with context %}
+<footer class="md-footer">
+  {% if page.previous_page or page.next_page %}
+    <div class="md-footer-nav">
+      <nav class="md-footer-nav__inner md-grid">
+        {% if page.previous_page %}
+          <a href="{{ page.previous_page.url | url }}" title="{{ page.previous_page.title | striptags }}" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
+            <div class="md-flex__cell md-flex__cell--shrink">
+              <i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
+            </div>
+            <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
+              <span class="md-flex__ellipsis">
+                <span class="md-footer-nav__direction">
+                  {{ lang.t("footer.previous") }}
+                </span>
+                {{ page.previous_page.title }}
+              </span>
+            </div>
+          </a>
+        {% endif %}
+        {% if page.next_page %}
+          <a href="{{ page.next_page.url | url }}" title="{{ page.next_page.title | striptags }}" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
+            <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
+              <span class="md-flex__ellipsis">
+                <span class="md-footer-nav__direction">
+                  {{ lang.t("footer.next") }}
+                </span>
+                {{ page.next_page.title }}
+              </span>
+            </div>
+            <div class="md-flex__cell md-flex__cell--shrink">
+              <i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
+            </div>
+          </a>
+        {% endif %}
+      </nav>
+    </div>
+  {% endif %}
+  <div class="md-footer-meta md-typeset">
+    <div class="md-footer-meta__inner md-grid">
+      <div class="md-footer-copyright">
+        {% if config.copyright %}
+          <div class="md-footer-copyright__highlight">
+            {{ config.copyright }}
+          </div>
+        {% endif %}
+      </div>
+      {% include "partials/social.html" %}
+    </div>
+  </div>
+</footer>
diff --git a/environment.yml b/environment.yml
new file mode 100644
index 0000000..17b040c
--- /dev/null
+++ b/environment.yml
@@ -0,0 +1,9 @@
+name: bumble
+channels:
+  - defaults
+  - conda-forge
+dependencies:
+  - pip=20
+  - python=3.8
+  - pip:
+    - --editable .[development,documentation,test]
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..36d5a1d
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,84 @@
+Bumble Examples
+===============
+
+NOTE:
+To run python scripts from this directory when the Bumble package isn't installed in your environment,
+put .. in your PYTHONPATH: `export PYTHONPATH=..`
+
+# `run_controller.py`
+Run two virtual controllers, one connected to a soft device written in python with a simple GATT server, and the other connected to an external host.
+
+## Running `run_controller.py` with a BlueZ host running on Linux.
+
+In this configuration, a BlueZ stack running on a Linux host is connected to a Bumble virtual
+controller, attached to a local link bus to a second, in-process, virtual controller, itself
+used by a virtual device with a GATT server.
+
+### Running with two separate hosts (ex: a mac laptop and a Linux VM)
+In this setup, the virtual controllers and host run on a mac desktop, and the BlueZ stack on a Linux VM. A UDP socket communicates HCI packets between the macOS host and the Linux guest.
+
+#### Linux setup
+In a terminal, run `socat` to bridge a UDP socket to a local PTY.
+The PTY is used a virtual HCI UART.
+(in this example, the mac's IP address seen from the Linux VM is `172.16.104.1`, replace it with
+the appropriate address for your environment. (you may also use a port number other than `22333` used here)
+```
+socat -d -d -x PTY,link=./hci_pty,rawer UDP-SENDTO:172.16.104.1:22333,bind=:22333
+```
+
+In the local directory, `socat` creates a symbolic link named `hci_pty` that points to the PTY.
+
+In a second terminal, run
+```
+sudo btattach -P h4 -B hci_pty
+```
+
+This tells BlueZ to use the PTY as an HCI UART controller.
+
+(optional) In a third terminal, run `sudo btmon`. This monitors the HCI traffic with BlueZ, which is great to see what's going on.
+
+In a fourth terminal, run `sudo bluetoothctl` to interact with BlueZ as a client. From there, you can scan, advertise, connect, etc.
+
+#### Mac setup
+In a macOS terminal, run
+```
+python run_controller.py device1.json udp:0.0.0.0:22333,172.16.104.161:22333
+```
+
+This configures one of the virtual controllers to use a UDP socket as its HCI transport. In this example, the ip address of the Linux VM is `172.16.104.161`, replace it with the appropriate
+address for your environment.
+
+Once both the Linux and macOS processes are started, you should be able to interact with the
+`bluetoothctl` tool on the Linux side and scan/connect/discover the virtual device running on
+the macOS side. Relevant log output in each of the terminal consoles should show what it going on.
+
+### Running with a single Linux host
+In setup, both the BlueZ stack and tools as well as the Bumble virtual stack are running on the same
+host.
+
+In a terminal, run the example as
+```
+python run_controller.py device1.json pty:hci_pty
+```
+
+In the local directory, a symbolic link named `hci_pty` that points to the PTY is created.
+
+From this point, run the same steps as in the previous example to attach the PTY to BlueZ and use
+`bluetoothctl` to interact with the virtual controller.
+
+
+# `run_gatt_client.py`
+Run a host application connected to a 'real' BLE controller over a UART HCI to a dev board running Zephyr in HCI mode (could be any other UART BLE controller, or BlueZ over a virtual UART). The application connects to a Bluetooth peer specified as an argument.
+Once connected, the application hosts a GATT client that discovers all services and all attributes of the peer and displays them.
+
+# `run_gatt_server.py`
+Run a host application connected to a 'real' BLE controller over a UART HCI to a dev board running Zephyr in HCI mode (could be any other UART BLE controller, or BlueZ over a virtual UART). The application connects to a Bluetooth peer specified as an argument.
+The application hosts a simple GATT server with basic
+services and characteristics.
+
+# `run_gatt_client_and_server.py`
+
+# `run_advertiser.py`
+
+# `run_scanner.py`
+Run a host application connected to a 'real' BLE controller over a UART HCI to a dev board running Zephyr in HCI mode (could be any other UART BLE controller, or BlueZ over a virtual UART), that starts scanning and prints out the scan results.
diff --git a/examples/a2dp_sink1.json b/examples/a2dp_sink1.json
new file mode 100644
index 0000000..61ce80d
--- /dev/null
+++ b/examples/a2dp_sink1.json
@@ -0,0 +1,5 @@
+{
+    "name": "Bumble Speaker",
+    "class_of_device": 2360324,
+    "keystore": "JsonKeyStore"
+}
diff --git a/examples/async_runner.py b/examples/async_runner.py
new file mode 100644
index 0000000..d0d1a12
--- /dev/null
+++ b/examples/async_runner.py
@@ -0,0 +1,85 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import os
+
+from bumble.utils import AsyncRunner
+
+# -----------------------------------------------------------------------------
+my_work_queue1 = AsyncRunner.WorkQueue()
+my_work_queue2 = AsyncRunner.WorkQueue(create_task=False)
+
+# -----------------------------------------------------------------------------
+@AsyncRunner.run_in_task()
+async def func1(x, y):
+    print('FUNC1: start', x, y)
+    await asyncio.sleep(x)
+    print('FUNC1: end', x, y)
+
+
+# -----------------------------------------------------------------------------
+@AsyncRunner.run_in_task(queue=my_work_queue1)
+async def func2(x, y):
+    print('FUNC2: start', x, y)
+    await asyncio.sleep(x)
+    print('FUNC2: end', x, y)
+
+
+# -----------------------------------------------------------------------------
+@AsyncRunner.run_in_task(queue=my_work_queue2)
+async def func3(x, y):
+    print('FUNC3: start', x, y)
+    await asyncio.sleep(x)
+    print('FUNC3: end', x, y)
+
+
+# -----------------------------------------------------------------------------
+@AsyncRunner.run_in_task(queue=None)
+async def func4(x, y):
+    print('FUNC4: start', x, y)
+    await asyncio.sleep(x)
+    print('FUNC4: end', x, y)
+
+    raise ValueError('test')
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    print("MAIN: start, loop=", asyncio.get_running_loop())
+    print("MAIN: invoke func1")
+    func1(1, 2)
+
+    print("MAIN: invoke func2")
+    func2(3, 4)
+
+    print("MAIN: invoke func3")
+    func3(5, 6)
+
+    print("MAIN: invoke func4")
+    func4(7, 8)
+
+    print("MAIN: sleeping 2 seconds")
+    await asyncio.sleep(2)
+    print("MAIN: running my_work_queue2.run")
+    await my_work_queue2.run()
+    print("MAIN: end (should never get here)")
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/battery_client.py b/examples/battery_client.py
new file mode 100644
index 0000000..888b23e
--- /dev/null
+++ b/examples/battery_client.py
@@ -0,0 +1,72 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+from colors import color
+from bumble.device import Device, Peer
+from bumble.transport import open_transport
+from bumble.profiles.battery_service import BatteryServiceProxy
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 3:
+        print('Usage: battery_client.py <transport-spec> <bluetooth-address>')
+        print('example: battery_client.py usb:0 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport(sys.argv[1]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create and start a device
+        device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+        await device.power_on()
+
+        # Connect to the peer
+        target_address = sys.argv[2]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address)
+        print(f'=== Connected to {connection}')
+
+        # Discover the Battery Service
+        peer = Peer(connection)
+        print('=== Discovering Battery Service')
+        battery_service = await peer.discover_and_create_service_proxy(BatteryServiceProxy)
+
+        # Check that the service was found
+        if not battery_service:
+            print('!!! Service not found')
+            return
+
+        # Subscribe to and read the battery level
+        if battery_service.battery_level:
+            await battery_service.battery_level.subscribe(
+                lambda value: print(f'{color("Battery Level Update:", "green")} {value}')
+            )
+            value = await battery_service.battery_level.read_value()
+            print(f'{color("Initial Battery Level:", "green")} {value}')
+
+        await hci_source.wait_for_termination()
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/battery_server.py b/examples/battery_server.py
new file mode 100644
index 0000000..3fabf0d
--- /dev/null
+++ b/examples/battery_server.py
@@ -0,0 +1,66 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+import random
+import struct
+
+from bumble.core import AdvertisingData
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.profiles.battery_service import BatteryService
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 3:
+        print('Usage: python battery_server.py <device-config> <transport-spec>')
+        print('example: python battery_server.py device1.json usb:0')
+        return
+
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+
+        # Add a Device Information Service and Battery Service to the GATT sever
+        battery_service = BatteryService(lambda _: random.randint(0, 100))
+        device.add_service(battery_service)
+
+        # Set the advertising data
+        device.advertising_data = bytes(
+            AdvertisingData([
+                (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble Battery', 'utf-8')),
+                (AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, bytes(battery_service.uuid)),
+                (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340))
+            ])
+        )
+
+        # Go!
+        await device.power_on()
+        await device.start_advertising(auto_restart=True)
+
+        # Notify every 3 seconds
+        while True:
+            await asyncio.sleep(3.0)
+            await device.notify_subscribers(battery_service.battery_level_characteristic)
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/classic1.json b/examples/classic1.json
new file mode 100644
index 0000000..811c9d5
--- /dev/null
+++ b/examples/classic1.json
@@ -0,0 +1,5 @@
+{
+    "name": "Bumble",
+    "class_of_device": 2360324,
+    "keystore": "JsonKeyStore"
+}
diff --git a/examples/classic2.json b/examples/classic2.json
new file mode 100644
index 0000000..dc828ea
--- /dev/null
+++ b/examples/classic2.json
@@ -0,0 +1,4 @@
+{
+    "name": "Bumble",
+    "class_of_device": 7936
+}
diff --git a/examples/device1.json b/examples/device1.json
new file mode 100644
index 0000000..8a6f405
--- /dev/null
+++ b/examples/device1.json
@@ -0,0 +1,7 @@
+{
+    "name": "Bumble",
+    "address": "F0:F1:F2:F3:F4:F5",
+    "advertising_interval": 2000,
+    "keystore": "JsonKeyStore",
+    "irk": "865F81FF5A8B486EAAE29A27AD9F77DC"
+}
diff --git a/examples/device2.json b/examples/device2.json
new file mode 100644
index 0000000..82869e0
--- /dev/null
+++ b/examples/device2.json
@@ -0,0 +1,8 @@
+{
+    "name": "Bumble2",
+    "address": "F6:F7:F8:F9:FA:FB",
+    "keystore": "JsonKeyStore",
+    "irk": "43E96EC5C5DBD8D0F5204CFFDECE0096",
+    "advertising_interval": 2000,
+    "advertising_data": "0201061106ba5689a6fabfa2bd01467d6e00fbabad08160a181604659b03"
+}
diff --git a/examples/device3.json b/examples/device3.json
new file mode 100644
index 0000000..d37819d
--- /dev/null
+++ b/examples/device3.json
@@ -0,0 +1,4 @@
+{
+    "name": "Bumble3",
+    "address": "F1:F2:F3:F4:F5:F6"
+}
diff --git a/examples/device_information_client.py b/examples/device_information_client.py
new file mode 100644
index 0000000..ed5892b
--- /dev/null
+++ b/examples/device_information_client.py
@@ -0,0 +1,80 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+from colors import color
+from bumble.device import Device, Peer
+from bumble.profiles.device_information_service import DeviceInformationServiceProxy
+from bumble.transport import open_transport
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 3:
+        print('Usage: device_information_client.py <transport-spec> <bluetooth-address>')
+        print('example: device_information_client.py usb:0 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport(sys.argv[1]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create and start a device
+        device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+        await device.power_on()
+
+        # Connect to the peer
+        target_address = sys.argv[2]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address)
+        print(f'=== Connected to {connection}')
+
+        # Discover the Device Information service
+        peer = Peer(connection)
+        print('=== Discovering Device Information Service')
+        device_information_service = await peer.discover_service_and_create_proxy(DeviceInformationServiceProxy)
+
+        # Check that the service was found
+        if device_information_service is None:
+            print('!!! Service not found')
+            return
+
+        # Read and print the fields
+        if device_information_service.manufacturer_name is not None:
+            print(color('Manufacturer Name:       ', 'green'), await device_information_service.manufacturer_name.read_value())
+        if device_information_service.model_number is not None:
+            print(color('Model Number:            ', 'green'), await device_information_service.model_number.read_value())
+        if device_information_service.serial_number is not None:
+            print(color('Serial Number:           ', 'green'), await device_information_service.serial_number.read_value())
+        if device_information_service.hardware_revision is not None:
+            print(color('Hardware Revision:       ', 'green'), await device_information_service.hardware_revision.read_value())
+        if device_information_service.firmware_revision is not None:
+            print(color('Firmware Revision:       ', 'green'), await device_information_service.firmware_revision.read_value())
+        if device_information_service.software_revision is not None:
+            print(color('Software Revision:       ', 'green'), await device_information_service.software_revision.read_value())
+        if device_information_service.system_id is not None:
+            print(color('System ID:               ', 'green'), await device_information_service.system_id.read_value())
+        if device_information_service.ieee_regulatory_certification_data_list is not None:
+            print(color('Regulatory Certification:', 'green'), (await device_information_service.ieee_regulatory_certification_data_list.read_value()).hex())
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/device_information_server.py b/examples/device_information_server.py
new file mode 100644
index 0000000..9c3b6b1
--- /dev/null
+++ b/examples/device_information_server.py
@@ -0,0 +1,66 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+import struct
+
+from bumble.core import AdvertisingData
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.profiles.device_information_service import DeviceInformationService
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 3:
+        print('Usage: python device_info_server.py <device-config> <transport-spec>')
+        print('example: python device_info_server.py device1.json usb:0')
+        return
+
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+
+        # Add a Device Information Service to the GATT sever
+        device_information_service = DeviceInformationService(
+            manufacturer_name = 'ACME',
+            model_number      = 'AB-102',
+            serial_number     = '7654321',
+            hardware_revision = '1.1.3',
+            software_revision = '2.5.6',
+            system_id         = (0x123456, 0x8877665544)
+        )
+        device.add_service(device_information_service)
+
+        # Set the advertising data
+        device.advertising_data = bytes(
+            AdvertisingData([
+                (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble Device', 'utf-8')),
+                (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340))
+            ])
+        )
+
+        # Go!
+        await device.power_on()
+        await device.start_advertising(auto_restart=True)
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/hfp_gateway.json b/examples/hfp_gateway.json
new file mode 100644
index 0000000..5e3d72b
--- /dev/null
+++ b/examples/hfp_gateway.json
@@ -0,0 +1,4 @@
+{
+    "name": "Bumble Phone",
+    "class_of_device": 6291980
+}
diff --git a/examples/hfp_handsfree.html b/examples/hfp_handsfree.html
new file mode 100644
index 0000000..ab1ce3d
--- /dev/null
+++ b/examples/hfp_handsfree.html
@@ -0,0 +1,79 @@
+<html>
+    <head>
+        <style>
+* {
+    font-family: sans-serif;
+}
+
+label {
+    display: block;
+}
+
+input, label {
+    margin: .4rem 0;
+}
+        </style>
+    </head>
+    <body>
+        Server Port <input id="port" type="text" value="8989"></input> <button onclick="connect()">Connect</button><br>
+        AT Command <input type="text" id="at_command" required size="10"> <button onclick="send_at_command()">Send</button><br>
+        Dial Phone Number <input type="text" id="dial_number" required size="10"> <button onclick="dial()">Dial</button><br>
+        <button onclick="answer()">Answer</button>
+        <button onclick="hangup()">Hang Up</button>
+        <button onclick="start_voice_assistant()">Start Voice Assistant</button>
+        <button onclick="stop_voice_assistant()">Stop Voice Assistant</button>
+        <hr>
+        <div id="socketState"></div>
+        <script>
+        let portInput = document.getElementById("port")
+        let atCommandInput = document.getElementById("at_command")
+        let dialNumberInput = document.getElementById("dial_number")
+        let socketState = document.getElementById("socketState")
+        let socket
+
+        function connect() {
+            socket = new WebSocket(`ws://localhost:${portInput.value}`);
+            socket.onopen = _ => {
+                socketState.innerText = 'OPEN'
+            }
+            socket.onclose = _ => {
+                socketState.innerText = 'CLOSED'
+            }
+            socket.onerror = (error) => {
+                socketState.innerText = 'ERROR'
+                console.log(`ERROR: ${error}`)
+            }
+        }
+
+        function send(message) {
+            if (socket && socket.readyState == WebSocket.OPEN) {
+                socket.send(JSON.stringify(message))
+            }
+        }
+
+        function send_at_command() {
+            send({ type:'at_command', command: atCommandInput.value })
+        }
+
+        function answer() {
+            send({ type:'at_command', command: 'ATA' })
+        }
+
+        function hangup() {
+            send({ type:'at_command', command: 'AT+CHUP' })
+        }
+
+        function dial() {
+            send({ type:'at_command', command: `ATD${dialNumberInput.value}` })
+        }
+
+        function start_voice_assistant() {
+            send(({ type:'at_command', command: 'AT+BVRA=1' }))
+        }
+
+        function stop_voice_assistant() {
+            send(({ type:'at_command', command: 'AT+BVRA=0' }))
+        }
+</script>
+    </body>
+</html>
\ No newline at end of file
diff --git a/examples/hfp_handsfree.json b/examples/hfp_handsfree.json
new file mode 100644
index 0000000..5d46a80
--- /dev/null
+++ b/examples/hfp_handsfree.json
@@ -0,0 +1,4 @@
+{
+    "name": "Bumble Hands-Free",
+    "class_of_device": 2360324
+}
diff --git a/examples/keyboard.html b/examples/keyboard.html
new file mode 100644
index 0000000..979de5e
--- /dev/null
+++ b/examples/keyboard.html
@@ -0,0 +1,61 @@
+<html>
+    <head>
+    </head>
+    <body>
+        Server Port <input id="port" type="text" value="8989"></input> <button onclick="connect()">Connect</button><br>
+        <div id="socketState"></div>
+        <div id="mouseInfo"></div>
+        <div id="keyInfo"></div>
+        <br>
+        <div id="frame" style="border: 2px solid; height:300"></div>
+        <script>
+            let portInput = document.getElementById("port")
+            let mouseInfo = document.getElementById("mouseInfo")
+            let ketInfo = document.getElementById("keyInfo")
+            let frame = document.getElementById("frame")
+            let socketState = document.getElementById("socketState")
+            let socket
+
+            frame.addEventListener('mousemove', onMouseMove)
+            document.addEventListener('keydown', onKeyDown)
+            document.addEventListener('keyup', onKeyUp)
+
+            function connect() {
+                socket = new WebSocket(`ws://localhost:${portInput.value}`);
+                socket.onopen = _ => {
+                    socketState.innerText = 'OPEN'
+                }
+                socket.onclose = _ => {
+                    socketState.innerText = 'CLOSED'
+                }
+                socket.onerror = (error) => {
+                    socketState.innerText = 'ERROR'
+                    console.log(`ERROR: ${error}`)
+                }
+            }
+
+            function send(message) {
+                if (socket && socket.readyState == WebSocket.OPEN) {
+                    socket.send(JSON.stringify(message))
+                }
+            }
+            function onMouseMove(event) {
+                //console.log(event.clientX, event.clientY)
+                mouseInfo.innerText = `MOUSE: x=${event.clientX}, y=${event.clientY}`
+                send({ type:'mousemove', x: event.clientX, y: event.clientY })
+            }
+
+            function onKeyDown(event) {
+                //console.log(event)
+                keyInfo.innerText = `KEYDOWN: ${event.key}`
+                send({ type:'keydown', key: event.key })
+            }
+
+            function onKeyUp(event) {
+                //console.log(event)
+                keyInfo.innerText = `KEYUP: ${event.key}`
+                send({ type:'keyup', key: event.key })
+            }
+        </script>
+    </body>
+</html>
\ No newline at end of file
diff --git a/examples/keyboard.json b/examples/keyboard.json
new file mode 100644
index 0000000..34fcab6
--- /dev/null
+++ b/examples/keyboard.json
@@ -0,0 +1,5 @@
+{
+    "name": "Bumble Keyboard",
+    "address": "F0:F1:F2:F3:F4:FA",
+    "advertising_interval": 200
+}
diff --git a/examples/keyboard.py b/examples/keyboard.py
new file mode 100644
index 0000000..ddb9f0f
--- /dev/null
+++ b/examples/keyboard.py
@@ -0,0 +1,359 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+import struct
+import websockets
+import json
+from colors import color
+
+from bumble.core import AdvertisingData
+from bumble.device import Device, Connection, Peer
+from bumble.utils import AsyncRunner
+from bumble.transport import open_transport_or_link
+from bumble.gatt import (
+    Descriptor,
+    Service,
+    Characteristic,
+    CharacteristicValue,
+    GATT_DEVICE_INFORMATION_SERVICE,
+    GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE,
+    GATT_DEVICE_BATTERY_SERVICE,
+    GATT_BATTERY_LEVEL_CHARACTERISTIC,
+    GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+    GATT_REPORT_CHARACTERISTIC,
+    GATT_REPORT_MAP_CHARACTERISTIC,
+    GATT_PROTOCOL_MODE_CHARACTERISTIC,
+    GATT_HID_INFORMATION_CHARACTERISTIC,
+    GATT_HID_CONTROL_POINT_CHARACTERISTIC,
+    GATT_REPORT_REFERENCE_DESCRIPTOR
+)
+
+# -----------------------------------------------------------------------------
+
+# Protocol Modes
+HID_BOOT_PROTOCOL   = 0x00
+HID_REPORT_PROTOCOL = 0x01
+
+# Report Types
+HID_INPUT_REPORT   = 0x01
+HID_OUTPUT_REPORT  = 0x02
+HID_FEATURE_REPORT = 0x03
+
+# Report Map
+HID_KEYBOARD_REPORT_MAP = bytes([
+    0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
+    0x09, 0x06,  # Usage (Keyboard)
+    0xA1, 0x01,  # Collection (Application)
+    0x85, 0x01,  # . Report ID (1)
+    0x05, 0x07,  # . Usage Page (Kbrd/Keypad)
+    0x19, 0xE0,  # . Usage Minimum (0xE0)
+    0x29, 0xE7,  # . Usage Maximum (0xE7)
+    0x15, 0x00,  # . Logical Minimum (0)
+    0x25, 0x01,  # . Logical Maximum (1)
+    0x75, 0x01,  # . Report Size (1)
+    0x95, 0x08,  # . Report Count (8)
+    0x81, 0x02,  # . Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x01,  # . Report Count (1)
+    0x75, 0x08,  # . Report Size (8)
+    0x81, 0x01,  # . Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x06,  # . Report Count (6)
+    0x75, 0x08,  # . Report Size (8)
+    0x15, 0x00,  # . Logical Minimum (0x00)
+    0x25, 0x94,  # . Logical Maximum (0x94)
+    0x05, 0x07,  # . Usage Page (Kbrd/Keypad)
+    0x19, 0x00,  # . Usage Minimum (0x00)
+    0x29, 0x94,  # . Usage Maximum (0x94)
+    0x81, 0x00,  # . Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0x95, 0x05,  # . Report Count (5)
+    0x75, 0x01,  # . Report Size (1)
+    0x05, 0x08,  # . Usage Page (LEDs)
+    0x19, 0x01,  # . Usage Minimum (Num Lock)
+    0x29, 0x05,  # . Usage Maximum (Kana)
+    0x91, 0x02,  # . Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+    0x95, 0x01,  # . Report Count (1)
+    0x75, 0x03,  # . Report Size (3)
+    0x91, 0x01,  # . Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+    0xC0         # End Collection
+])
+
+
+# -----------------------------------------------------------------------------
+class ServerListener(Device.Listener, Connection.Listener):
+    def __init__(self, device):
+        self.device = device
+
+    @AsyncRunner.run_in_task()
+    async def on_connection(self, connection):
+        print(f'=== Connected to {connection}')
+        connection.listener = self
+
+    @AsyncRunner.run_in_task()
+    async def on_disconnection(self, reason):
+        print(f'### Disconnected, reason={reason}')
+
+
+# -----------------------------------------------------------------------------
+def on_hid_control_point_write(connection, value):
+    print(f'Control Point Write: {value}')
+
+
+# -----------------------------------------------------------------------------
+def on_report(characteristic, value):
+    print(color('Report:', 'cyan'), value.hex(), 'from', characteristic)
+
+
+# -----------------------------------------------------------------------------
+async def keyboard_host(device, peer_address):
+    await device.power_on()
+    connection = await device.connect(peer_address)
+    await connection.pair()
+    peer = Peer(connection)
+    await peer.discover_service(GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE)
+    hid_services = peer.get_services_by_uuid(GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE)
+    if not hid_services:
+        print(color('!!! No HID service', 'red'))
+        return
+    await peer.discover_characteristics()
+
+    protocol_mode_characteristics = peer.get_characteristics_by_uuid(GATT_PROTOCOL_MODE_CHARACTERISTIC)
+    if not protocol_mode_characteristics:
+        print(color('!!! No Protocol Mode characteristic', 'red'))
+        return
+    protocol_mode_characteristic = protocol_mode_characteristics[0]
+
+    hid_information_characteristics = peer.get_characteristics_by_uuid(GATT_HID_INFORMATION_CHARACTERISTIC)
+    if not hid_information_characteristics:
+        print(color('!!! No HID Information characteristic', 'red'))
+        return
+    hid_information_characteristic = hid_information_characteristics[0]
+
+    report_map_characteristics = peer.get_characteristics_by_uuid(GATT_REPORT_MAP_CHARACTERISTIC)
+    if not report_map_characteristics:
+        print(color('!!! No Report Map characteristic', 'red'))
+        return
+    report_map_characteristic = report_map_characteristics[0]
+
+    control_point_characteristics = peer.get_characteristics_by_uuid(GATT_HID_CONTROL_POINT_CHARACTERISTIC)
+    if not control_point_characteristics:
+        print(color('!!! No Control Point characteristic', 'red'))
+        return
+    # control_point_characteristic = control_point_characteristics[0]
+
+    report_characteristics = peer.get_characteristics_by_uuid(GATT_REPORT_CHARACTERISTIC)
+    if not report_characteristics:
+        print(color('!!! No Report characteristic', 'red'))
+        return
+    for i, characteristic in enumerate(report_characteristics):
+        print(color('REPORT:', 'yellow'), characteristic)
+        if characteristic.properties & Characteristic.NOTIFY:
+            await peer.discover_descriptors(characteristic)
+            report_reference_descriptor = characteristic.get_descriptor(GATT_REPORT_REFERENCE_DESCRIPTOR)
+            if report_reference_descriptor:
+                report_reference = await peer.read_value(report_reference_descriptor)
+                print(color('  Report Reference:', 'blue'), report_reference.hex())
+            else:
+                report_reference = bytes([0, 0])
+            await peer.subscribe(characteristic, lambda value, param=f'[{i}] {report_reference.hex()}': on_report(param, value))
+
+    protocol_mode = await peer.read_value(protocol_mode_characteristic)
+    print(f'Protocol Mode: {protocol_mode.hex()}')
+    hid_information = await peer.read_value(hid_information_characteristic)
+    print(f'HID Information: {hid_information.hex()}')
+    report_map = await peer.read_value(report_map_characteristic)
+    print(f'Report Map: {report_map.hex()}')
+
+    await asyncio.get_running_loop().create_future()
+
+
+# -----------------------------------------------------------------------------
+async def keyboard_device(device, command):
+    # Create an 'input report' characteristic to send keyboard reports to the host
+    input_report_characteristic = Characteristic(
+        GATT_REPORT_CHARACTERISTIC,
+        Characteristic.READ | Characteristic.WRITE | Characteristic.NOTIFY,
+        Characteristic.READABLE | Characteristic.WRITEABLE,
+        bytes([0, 0, 0, 0, 0, 0, 0, 0]),
+        [
+            Descriptor(GATT_REPORT_REFERENCE_DESCRIPTOR, Descriptor.READABLE, bytes([0x01, HID_INPUT_REPORT]))
+        ]
+    )
+
+    # Create an 'output report' characteristic to receive keyboard reports from the host
+    output_report_characteristic = Characteristic(
+        GATT_REPORT_CHARACTERISTIC,
+        Characteristic.READ | Characteristic.WRITE | Characteristic.WRITE_WITHOUT_RESPONSE,
+        Characteristic.READABLE | Characteristic.WRITEABLE,
+        bytes([0]),
+        [
+            Descriptor(GATT_REPORT_REFERENCE_DESCRIPTOR, Descriptor.READABLE, bytes([0x01, HID_OUTPUT_REPORT]))
+        ]
+    )
+
+    # Add the services to the GATT sever
+    device.add_services([
+        Service(
+            GATT_DEVICE_INFORMATION_SERVICE,
+            [
+                Characteristic(
+                    GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+                    Characteristic.READ,
+                    Characteristic.READABLE,
+                    'Bumble'
+                )
+            ]
+        ),
+        Service(
+            GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE,
+            [
+                Characteristic(
+                    GATT_PROTOCOL_MODE_CHARACTERISTIC,
+                    Characteristic.READ,
+                    Characteristic.READABLE,
+                    bytes([HID_REPORT_PROTOCOL])
+                ),
+                Characteristic(
+                    GATT_HID_INFORMATION_CHARACTERISTIC,
+                    Characteristic.READ,
+                    Characteristic.READABLE,
+                    bytes([0x11, 0x01, 0x00, 0x03])  # bcdHID=1.1, bCountryCode=0x00, Flags=RemoteWake|NormallyConnectable
+                ),
+                Characteristic(
+                    GATT_HID_CONTROL_POINT_CHARACTERISTIC,
+                    Characteristic.WRITE_WITHOUT_RESPONSE,
+                    Characteristic.WRITEABLE,
+                    CharacteristicValue(write=on_hid_control_point_write)
+                ),
+                Characteristic(
+                    GATT_REPORT_MAP_CHARACTERISTIC,
+                    Characteristic.READ,
+                    Characteristic.READABLE,
+                    HID_KEYBOARD_REPORT_MAP
+                ),
+                input_report_characteristic,
+                output_report_characteristic
+            ]
+        ),
+        Service(
+            GATT_DEVICE_BATTERY_SERVICE,
+            [
+                Characteristic(
+                    GATT_BATTERY_LEVEL_CHARACTERISTIC,
+                    Characteristic.READ,
+                    Characteristic.READABLE,
+                    bytes([100])
+                )
+            ]
+        )
+    ])
+
+    # Debug print
+    for attribute in device.gatt_server.attributes:
+        print(attribute)
+
+    # Set the advertising data
+    device.advertising_data = bytes(
+        AdvertisingData([
+            (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble Keyboard', 'utf-8')),
+            (AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
+                bytes(GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE)),
+            (AdvertisingData.APPEARANCE, struct.pack('<H', 0x03C1)),
+            (AdvertisingData.FLAGS, bytes([0x05]))
+        ])
+    )
+
+    # Attach a listener
+    device.listener = ServerListener(device)
+
+    # Go!
+    await device.power_on()
+    await device.start_advertising(auto_restart=True)
+
+    if command == 'web':
+        # Start a Websocket server to receive events from a web page
+        async def serve(websocket, path):
+            while True:
+                try:
+                    message = await websocket.recv()
+                    print('Received: ', str(message))
+
+                    parsed = json.loads(message)
+                    message_type = parsed['type']
+                    if message_type == 'keydown':
+                        # Only deal with keys a to z for now
+                        key = parsed['key']
+                        if len(key) == 1:
+                            code = ord(key)
+                            if code >= ord('a') and code <= ord('z'):
+                                hid_code = 0x04 + code - ord('a')
+                                input_report_characteristic.value = bytes([0, 0, hid_code, 0, 0, 0, 0, 0])
+                                await device.notify_subscribers(input_report_characteristic)
+                    elif message_type == 'keyup':
+                        input_report_characteristic.value = bytes.fromhex('0000000000000000')
+                        await device.notify_subscribers(input_report_characteristic)
+
+                except websockets.exceptions.ConnectionClosedOK:
+                    pass
+        await websockets.serve(serve, 'localhost', 8989)
+        await asyncio.get_event_loop().create_future()
+    else:
+        message = bytes('hello', 'ascii')
+        while True:
+            for letter in message:
+                await asyncio.sleep(3.0)
+
+                # Keypress for the letter
+                keycode = 0x04 + letter - 0x61
+                input_report_characteristic.value = bytes([0, 0, keycode, 0, 0, 0, 0, 0])
+                await device.notify_subscribers(input_report_characteristic)
+
+                # Key release
+                input_report_characteristic.value = bytes.fromhex('0000000000000000')
+                await device.notify_subscribers(input_report_characteristic)
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 4:
+        print('Usage: python keyboard.py <device-config> <transport-spec> <command>')
+        print('  where <command> is one of:')
+        print('  connect <address> (run a keyboard host, connecting to a keyboard)')
+        print('  web (run a keyboard with keypress input from a web page, see keyboard.html')
+        print('  sim (run a keyboard simulation, emitting a canned sequence of keystrokes')
+        print('example: python keyboard.py keyboard.json usb:0 sim')
+        print('example: python keyboard.py keyboard.json usb:0 connect A0:A1:A2:A3:A4:A5')
+        return
+
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        # Create a device to manage the host
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+
+        command = sys.argv[3]
+        if command == 'connect':
+            # Run as a Keyboard host
+            await keyboard_host(device, sys.argv[4])
+        elif command in {'sim', 'web'}:
+            # Run as a keyboard device
+            await keyboard_device(device, command)
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_a2dp_info.py b/examples/run_a2dp_info.py
new file mode 100644
index 0000000..0d6f66b
--- /dev/null
+++ b/examples/run_a2dp_info.py
@@ -0,0 +1,188 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from colors import color
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import (
+    BT_BR_EDR_TRANSPORT,
+    BT_AVDTP_PROTOCOL_ID,
+    BT_AUDIO_SINK_SERVICE,
+    BT_L2CAP_PROTOCOL_ID
+)
+from bumble.avdtp import (
+    Protocol as AVDTP_Protocol,
+    find_avdtp_service_with_connection
+)
+from bumble.a2dp import make_audio_source_service_sdp_records
+from bumble.sdp import (
+    Client as SDP_Client,
+    ServiceAttribute,
+    DataElement,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+)
+
+
+# -----------------------------------------------------------------------------
+def sdp_records():
+    service_record_handle = 0x00010001
+    return {
+        service_record_handle: make_audio_source_service_sdp_records(service_record_handle)
+    }
+
+
+# -----------------------------------------------------------------------------
+async def find_a2dp_service(device, connection):
+    # Connect to the SDP Server
+    sdp_client = SDP_Client(device)
+    await sdp_client.connect(connection)
+
+    # Search for services with an Audio Sink service class
+    search_result = await sdp_client.search_attributes(
+        [BT_AUDIO_SINK_SERVICE],
+        [
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+        ]
+    )
+
+    print(color('==================================', 'blue'))
+    print(color('A2DP Sink Services:', 'yellow'))
+
+    service_version = None
+
+    for attribute_list in search_result:
+        print(color('SERVICE:', 'green'))
+
+        # Service classes
+        service_class_id_list = ServiceAttribute.find_attribute_in_list(
+            attribute_list,
+            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+        )
+        if service_class_id_list:
+            if service_class_id_list.value:
+                print(color('  Service Classes:', 'green'))
+                for service_class_id in service_class_id_list.value:
+                    print('   ', service_class_id.value)
+
+        # Protocol info
+        protocol_descriptor_list = ServiceAttribute.find_attribute_in_list(
+            attribute_list,
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID
+        )
+        if protocol_descriptor_list:
+            print(color('  Protocol:', 'green'))
+            for protocol_descriptor in protocol_descriptor_list.value:
+                if protocol_descriptor.value[0].value == BT_L2CAP_PROTOCOL_ID:
+                    if len(protocol_descriptor.value) >= 2:
+                        psm = protocol_descriptor.value[1].value
+                        print(f'{color("    L2CAP PSM:", "cyan")}     {psm}')
+                elif protocol_descriptor.value[0].value == BT_AVDTP_PROTOCOL_ID:
+                    if len(protocol_descriptor.value) >= 2:
+                        avdtp_version_major = protocol_descriptor.value[1].value >> 8
+                        avdtp_version_minor = protocol_descriptor.value[1].value & 0xFF
+                        print(f'{color("    AVDTP Version:", "cyan")} {avdtp_version_major}.{avdtp_version_minor}')
+                        service_version = (avdtp_version_major, avdtp_version_minor)
+
+        # Profile info
+        bluetooth_profile_descriptor_list = ServiceAttribute.find_attribute_in_list(
+            attribute_list,
+            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+        )
+        if bluetooth_profile_descriptor_list:
+            if bluetooth_profile_descriptor_list.value:
+                if bluetooth_profile_descriptor_list.value[0].type == DataElement.SEQUENCE:
+                    bluetooth_profile_descriptors = bluetooth_profile_descriptor_list.value
+                else:
+                    # Sometimes, instead of a list of lists, we just find a list. Fix that
+                    bluetooth_profile_descriptors = [bluetooth_profile_descriptor_list]
+
+                print(color('  Profiles:', 'green'))
+                for bluetooth_profile_descriptor in bluetooth_profile_descriptors:
+                    version_major = bluetooth_profile_descriptor.value[1].value >> 8
+                    version_minor = bluetooth_profile_descriptor.value[1].value & 0xFF
+                    print(f'    {bluetooth_profile_descriptor.value[0].value} - version {version_major}.{version_minor}')
+
+    await sdp_client.disconnect()
+    return service_version
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 4:
+        print('Usage: run_a2dp_info.py <device-config> <transport-spec> <bt-addr>')
+        print('example: run_a2dp_info.py classic1.json usb:0 14:7D:DA:4E:53:A8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+
+        # Start the controller
+        await device.power_on()
+
+        # Setup the SDP to expose a SRC service, in case the remote device queries us back
+        device.sdp_service_records = sdp_records()
+
+        # Connect to a peer
+        target_address = sys.argv[3]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+        print(f'=== Connected to {connection.peer_address}!')
+
+        # Request authentication
+        print('*** Authenticating...')
+        await connection.authenticate()
+        print('*** Authenticated')
+
+        # Enable encryption
+        print('*** Enabling encryption...')
+        await connection.encrypt()
+        print('*** Encryption on')
+
+        # Look for an A2DP service
+        avdtp_version = await find_a2dp_service(device, connection)
+        if not avdtp_version:
+            print(color('!!! no AVDTP service found'))
+            return
+        print(f'AVDTP version: {avdtp_version[0]}.{avdtp_version[1]}')
+
+        # Create a client to interact with the remote device
+        client = await AVDTP_Protocol.connect(connection, avdtp_version)
+
+        # Discover all endpoints on the remote device
+        endpoints = await client.discover_remote_endpoints()
+        print(f'@@@ Found {len(endpoints)} endpoints')
+        for endpoint in endpoints:
+            print('@@@', endpoint)
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_a2dp_sink.py b/examples/run_a2dp_sink.py
new file mode 100644
index 0000000..bc193a1
--- /dev/null
+++ b/examples/run_a2dp_sink.py
@@ -0,0 +1,163 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from colors import color
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import BT_BR_EDR_TRANSPORT
+from bumble.avdtp import (
+    AVDTP_AUDIO_MEDIA_TYPE,
+    Protocol,
+    Listener,
+    MediaCodecCapabilities
+)
+from bumble.a2dp import (
+    make_audio_sink_service_sdp_records,
+    A2DP_SBC_CODEC_TYPE,
+    SBC_MONO_CHANNEL_MODE,
+    SBC_DUAL_CHANNEL_MODE,
+    SBC_SNR_ALLOCATION_METHOD,
+    SBC_LOUDNESS_ALLOCATION_METHOD,
+    SBC_STEREO_CHANNEL_MODE,
+    SBC_JOINT_STEREO_CHANNEL_MODE,
+    SbcMediaCodecInformation
+)
+
+Context = {
+    'output': None
+}
+
+
+# -----------------------------------------------------------------------------
+def sdp_records():
+    service_record_handle = 0x00010001
+    return {
+        service_record_handle: make_audio_sink_service_sdp_records(service_record_handle)
+    }
+
+
+# -----------------------------------------------------------------------------
+def codec_capabilities():
+    # NOTE: this shouldn't be hardcoded, but passed on the command line instead
+    return MediaCodecCapabilities(
+        media_type                = AVDTP_AUDIO_MEDIA_TYPE,
+        media_codec_type          = A2DP_SBC_CODEC_TYPE,
+        media_codec_information   = SbcMediaCodecInformation.from_lists(
+            sampling_frequencies  = [48000, 44100, 32000, 16000],
+            channel_modes         = [
+                SBC_MONO_CHANNEL_MODE,
+                SBC_DUAL_CHANNEL_MODE,
+                SBC_STEREO_CHANNEL_MODE,
+                SBC_JOINT_STEREO_CHANNEL_MODE
+            ],
+            block_lengths         = [4, 8, 12, 16],
+            subbands              = [4, 8],
+            allocation_methods    = [SBC_LOUDNESS_ALLOCATION_METHOD, SBC_SNR_ALLOCATION_METHOD],
+            minimum_bitpool_value = 2,
+            maximum_bitpool_value = 53
+        )
+    )
+
+
+# -----------------------------------------------------------------------------
+def on_avdtp_connection(server):
+    # Add a sink endpoint to the server
+    sink = server.add_sink(codec_capabilities())
+    sink.on('rtp_packet', on_rtp_packet)
+
+
+# -----------------------------------------------------------------------------
+def on_rtp_packet(packet):
+    header           = packet.payload[0]
+    fragmented       = header >> 7
+    start            = (header >> 6) & 0x01
+    last             = (header >> 5) & 0x01
+    number_of_frames = header & 0x0F
+
+    if fragmented:
+        print(f'RTP: fragment {number_of_frames}')
+    else:
+        print(f'RTP: {number_of_frames} frames')
+
+    Context['output'].write(packet.payload[1:])
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 4:
+        print('Usage: run_a2dp_sink.py <device-config> <transport-spec> <sbc-file> [<bt-addr>]')
+        print('example: run_a2dp_sink.py classic1.json usb:0 output.sbc')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        with open(sys.argv[3], 'wb') as sbc_file:
+            Context['output'] = sbc_file
+
+            # Create a device
+            device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+            device.classic_enabled = True
+
+            # Setup the SDP to expose the sink service
+            device.sdp_service_records = sdp_records()
+
+            # Start the controller
+            await device.power_on()
+
+            # Create a listener to wait for AVDTP connections
+            listener = Listener(Listener.create_registrar(device))
+            listener.on('connection', on_avdtp_connection)
+
+            if len(sys.argv) >= 5:
+                # Connect to the source
+                target_address = sys.argv[4]
+                print(f'=== Connecting to {target_address}...')
+                connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+                print(f'=== Connected to {connection.peer_address}!')
+
+                # Request authentication
+                print('*** Authenticating...')
+                await connection.authenticate()
+                print('*** Authenticated')
+
+                # Enable encryption
+                print('*** Enabling encryption...')
+                await connection.encrypt()
+                print('*** Encryption on')
+
+                server = await Protocol.connect(connection)
+                listener.set_server(connection, server)
+                sink = server.add_sink(codec_capabilities())
+                sink.on('rtp_packet', on_rtp_packet)
+            else:
+                # Start being discoverable and connectable
+                await device.set_discoverable(True)
+                await device.set_connectable(True)
+
+            await hci_source.wait_for_termination()
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_a2dp_source.py b/examples/run_a2dp_source.py
new file mode 100644
index 0000000..45abad9
--- /dev/null
+++ b/examples/run_a2dp_source.py
@@ -0,0 +1,175 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from colors import color
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import BT_BR_EDR_TRANSPORT
+from bumble.avdtp import (
+    find_avdtp_service_with_connection,
+    AVDTP_AUDIO_MEDIA_TYPE,
+    MediaCodecCapabilities,
+    MediaPacketPump,
+    Protocol,
+    Listener
+)
+from bumble.a2dp import (
+    SBC_JOINT_STEREO_CHANNEL_MODE,
+    SBC_LOUDNESS_ALLOCATION_METHOD,
+    make_audio_source_service_sdp_records,
+    A2DP_SBC_CODEC_TYPE,
+    SbcMediaCodecInformation,
+    SbcPacketSource
+)
+
+
+# -----------------------------------------------------------------------------
+def sdp_records():
+    service_record_handle = 0x00010001
+    return {
+        service_record_handle: make_audio_source_service_sdp_records(service_record_handle)
+    }
+
+
+# -----------------------------------------------------------------------------
+def codec_capabilities():
+    # NOTE: this shouldn't be hardcoded, but should be inferred from the input file instead
+    return MediaCodecCapabilities(
+        media_type                = AVDTP_AUDIO_MEDIA_TYPE,
+        media_codec_type          = A2DP_SBC_CODEC_TYPE,
+        media_codec_information   = SbcMediaCodecInformation.from_discrete_values(
+            sampling_frequency    = 44100,
+            channel_mode          = SBC_JOINT_STEREO_CHANNEL_MODE,
+            block_length          = 16,
+            subbands              = 8,
+            allocation_method     = SBC_LOUDNESS_ALLOCATION_METHOD,
+            minimum_bitpool_value = 2,
+            maximum_bitpool_value = 53
+        )
+    )
+
+
+# -----------------------------------------------------------------------------
+def on_avdtp_connection(read_function, protocol):
+    packet_source = SbcPacketSource(read_function, protocol.l2cap_channel.mtu, codec_capabilities())
+    packet_pump = MediaPacketPump(packet_source.packets)
+    protocol.add_source(packet_source.codec_capabilities, packet_pump)
+
+
+# -----------------------------------------------------------------------------
+async def stream_packets(read_function, protocol):
+    # Discover all endpoints on the remote device
+    endpoints = await protocol.discover_remote_endpoints()
+    for endpoint in endpoints:
+        print('@@@', endpoint)
+
+    # Select a sink
+    sink = protocol.find_remote_sink_by_codec(AVDTP_AUDIO_MEDIA_TYPE, A2DP_SBC_CODEC_TYPE)
+    if sink is None:
+        print(color('!!! no SBC sink found', 'red'))
+        return
+    print(f'### Selected sink: {sink.seid}')
+
+    # Stream the packets
+    packet_source = SbcPacketSource(read_function, protocol.l2cap_channel.mtu, codec_capabilities())
+    packet_pump = MediaPacketPump(packet_source.packets)
+    source = protocol.add_source(packet_source.codec_capabilities, packet_pump)
+    stream = await protocol.create_stream(source, sink)
+    await stream.start()
+    await asyncio.sleep(5)
+    await stream.stop()
+    await asyncio.sleep(5)
+    await stream.start()
+    await asyncio.sleep(5)
+    await stream.stop()
+    await stream.close()
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 4:
+        print('Usage: run_a2dp_source.py <device-config> <transport-spec> <sbc-file> [<bluetooth-address>]')
+        print('example: run_a2dp_source.py classic1.json usb:0 test.sbc E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+
+        # Setup the SDP to expose the SRC service
+        device.sdp_service_records = sdp_records()
+
+        # Start
+        await device.power_on()
+
+        with open(sys.argv[3], 'rb') as sbc_file:
+            # NOTE: this should be using asyncio file reading, but blocking reads are good enough for testing
+            async def read(byte_count):
+                return sbc_file.read(byte_count)
+
+            if len(sys.argv) > 4:
+                # Connect to a peer
+                target_address = sys.argv[4]
+                print(f'=== Connecting to {target_address}...')
+                connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+                print(f'=== Connected to {connection.peer_address}!')
+
+                # Request authentication
+                print('*** Authenticating...')
+                await connection.authenticate()
+                print('*** Authenticated')
+
+                # Enable encryption
+                print('*** Enabling encryption...')
+                await connection.encrypt()
+                print('*** Encryption on')
+
+                # Look for an A2DP service
+                avdtp_version = await find_avdtp_service_with_connection(device, connection)
+                if not avdtp_version:
+                    print(color('!!! no A2DP service found'))
+                    return
+
+                # Create a client to interact with the remote device
+                protocol = await Protocol.connect(connection, avdtp_version)
+
+                # Start streaming
+                await stream_packets(read, protocol)
+            else:
+                # Create a listener to wait for AVDTP connections
+                listener = Listener(Listener.create_registrar(device), version=(1, 2))
+                listener.on('connection', lambda protocol: on_avdtp_connection(read, protocol))
+
+                # Become connectable and wait for a connection
+                await device.set_discoverable(True)
+                await device.set_connectable(True)
+
+            await hci_source.wait_for_termination()
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_advertiser.py b/examples/run_advertiser.py
new file mode 100644
index 0000000..5201356
--- /dev/null
+++ b/examples/run_advertiser.py
@@ -0,0 +1,48 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+
+from bumble.hci import *
+from bumble.controller import *
+from bumble.device import *
+from bumble.transport import *
+from bumble.host import *
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 3:
+        print('Usage: run_advertiser.py <config-file> <transport-spec>')
+        print('example: run_advertiser.py device1.json link-relay:ws://localhost:8888/test')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        await device.power_on()
+        await device.start_advertising()
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_classic_connect.py b/examples/run_classic_connect.py
new file mode 100644
index 0000000..d6842fe
--- /dev/null
+++ b/examples/run_classic_connect.py
@@ -0,0 +1,81 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+from colors import color
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import BT_BR_EDR_TRANSPORT, BT_L2CAP_PROTOCOL_ID
+from bumble.sdp import Client as SDP_Client, SDP_PUBLIC_BROWSE_ROOT, SDP_ALL_ATTRIBUTES_RANGE
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_classic_connect.py <device-config> <transport-spec> <bluetooth-address>')
+        print('example: run_classic_connect.py classic1.json usb:04b4:f901 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+        await device.power_on()
+
+        # Connect to a peer
+        target_address = sys.argv[3]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+        print(f'=== Connected to {connection.peer_address}!')
+
+        # Connect to the SDP Server
+        sdp_client = SDP_Client(device)
+        await sdp_client.connect(connection)
+
+        # List all services in the root browse group
+        service_record_handles = await sdp_client.search_services([SDP_PUBLIC_BROWSE_ROOT])
+        print(color('\n==================================', 'blue'))
+        print(color('SERVICES:', 'yellow'), service_record_handles)
+
+        # For each service in the root browse group, get all its attributes
+        for service_record_handle in service_record_handles:
+            attributes = await sdp_client.get_attributes(service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE])
+            print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow'))
+            for attribute in attributes:
+                print('  ', attribute.to_string(color=True))
+
+        # Search for services with an L2CAP service attribute
+        search_result = await sdp_client.search_attributes([BT_L2CAP_PROTOCOL_ID], [SDP_ALL_ATTRIBUTES_RANGE])
+        print(color('\n==================================', 'blue'))
+        print(color('SEARCH RESULTS:', 'yellow'))
+        for attribute_list in search_result:
+            print(color('SERVICE:', 'green'))
+            print('  ' + '\n  '.join([attribute.to_string(color=True) for attribute in attribute_list]))
+
+        await sdp_client.disconnect()
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_classic_discoverable.py b/examples/run_classic_discoverable.py
new file mode 100644
index 0000000..5cdbc27
--- /dev/null
+++ b/examples/run_classic_discoverable.py
@@ -0,0 +1,104 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.sdp import (
+    DataElement,
+    ServiceAttribute,
+    SDP_PUBLIC_BROWSE_ROOT,
+    SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
+    SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+)
+from bumble.core import (
+    BT_AUDIO_SINK_SERVICE,
+    BT_L2CAP_PROTOCOL_ID,
+    BT_AVDTP_PROTOCOL_ID,
+    BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE
+)
+
+# -----------------------------------------------------------------------------
+SDP_SERVICE_RECORDS = {
+    0x00010001: [
+        ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(0x00010001)),
+        ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([
+            DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)
+        ])),
+        ServiceAttribute(
+            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+            DataElement.sequence([DataElement.uuid(BT_AUDIO_SINK_SERVICE)])
+        ),
+        ServiceAttribute(
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            DataElement.sequence([
+                DataElement.sequence([
+                    DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
+                    DataElement.unsigned_integer_16(25)
+                ]),
+                DataElement.sequence([
+                    DataElement.uuid(BT_AVDTP_PROTOCOL_ID),
+                    DataElement.unsigned_integer_16(256)
+                ])
+            ])
+        ),
+        ServiceAttribute(
+            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            DataElement.sequence([
+                DataElement.sequence([
+                    DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE),
+                    DataElement.unsigned_integer_16(256)
+                ])
+            ])
+        )
+    ]
+}
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_classic_discoverable.py <device-config> <transport-spec>')
+        print('example: run_classic_discoverable.py classic1.json usb:04b4:f901')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+        device.sdp_service_records = SDP_SERVICE_RECORDS
+        await device.power_on()
+
+        # Start being discoverable and connectable
+        await device.set_discoverable(True)
+        await device.set_connectable(True)
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_classic_discovery.py b/examples/run_classic_discovery.py
new file mode 100644
index 0000000..b0ab5ee
--- /dev/null
+++ b/examples/run_classic_discovery.py
@@ -0,0 +1,64 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+from colors import color
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import DeviceClass
+
+
+# -----------------------------------------------------------------------------
+class DiscoveryListener(Device.Listener):
+    def on_inquiry_result(self, address, class_of_device, eir_data, rssi):
+        service_classes, major_device_class, minor_device_class = DeviceClass.split_class_of_device(class_of_device)
+        separator = '\n  '
+        print(f'>>> {color(address, "yellow")}:')
+        print(f'  Device Class (raw): {class_of_device:06X}')
+        print(f'  Device Major Class: {DeviceClass.major_device_class_name(major_device_class)}')
+        print(f'  Device Minor Class: {DeviceClass.minor_device_class_name(major_device_class, minor_device_class)}')
+        print(f'  Device Services: {", ".join(DeviceClass.service_class_labels(service_classes))}')
+        print(f'  RSSI: {rssi}')
+        if eir_data.ad_structures:
+            print(f'  {eir_data.to_string(separator)}')
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 2:
+        print('Usage: run_classic_discovery.py <transport-spec>')
+        print('example: run_classic_discovery.py usb:04b4:f901')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[1]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+        device.listener = DiscoveryListener()
+        await device.power_on()
+        await device.start_discovery()
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_connect_and_encrypt.py b/examples/run_connect_and_encrypt.py
new file mode 100644
index 0000000..0ee868a
--- /dev/null
+++ b/examples/run_connect_and_encrypt.py
@@ -0,0 +1,58 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_connect_and_encrypt.py <device-config> <transport-spec> <bluetooth-address>')
+        print('example: run_connect_and_encrypt.py device1.json usb:0 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        await device.power_on()
+
+        # Connect to the peer
+        target_address = sys.argv[3]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address)
+        print('=== Connected')
+        print('*** Encrypting...')
+        try:
+            await connection.encrypt()
+        except Exception as error:
+            print(f'!!! Encryption failed: {error}')
+            return
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_controller.py b/examples/run_controller.py
new file mode 100644
index 0000000..d8295d4
--- /dev/null
+++ b/examples/run_controller.py
@@ -0,0 +1,87 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import sys
+import os
+
+from bumble.hci import *
+from bumble.controller import *
+from bumble.host import *
+from bumble.device import *
+from bumble.transport import *
+from bumble.link import *
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 4:
+        print('Usage: run_controller.py <controller-address> <device-config> <transport-spec>')
+        print('example: run_controller.py F2:F3:F4:F5:F6:F7 device1.json udp:0.0.0.0:22333,172.16.104.161:22333')
+        return
+
+    print('>>> connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[3]) as (hci_source, hci_sink):
+        print('>>> connected')
+
+        # Create a local link
+        link = LocalLink()
+
+        # Create a first controller using the packet source/sink as its host interface
+        controller1 = Controller('C1', host_source = hci_source, host_sink = hci_sink, link = link)
+        controller1.random_address = sys.argv[1]
+
+        # Create a second controller using the same link
+        controller2 = Controller('C2', link = link)
+
+        # Create a host for the second controller
+        host = Host()
+        host.controller = controller2
+
+        # Create a device to manage the host
+        device = Device.from_config_file(sys.argv[2])
+        device.host = host
+
+        # Add some basic services to the device's GATT server
+        descriptor = Descriptor(GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, Descriptor.READABLE, 'My Description')
+        manufacturer_name_characteristic = Characteristic(
+            GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+            Characteristic.READ,
+            Characteristic.READABLE,
+            "Fitbit",
+            [descriptor]
+        )
+        device_info_service = Service(GATT_DEVICE_INFORMATION_SERVICE, [
+            manufacturer_name_characteristic
+        ])
+        device.add_service(device_info_service)
+
+        # Debug print
+        for attribute in device.gatt_server.attributes:
+            print(attribute)
+
+        await device.power_on()
+        await device.start_advertising()
+        await device.start_scanning()
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_controller_with_scanner.py b/examples/run_controller_with_scanner.py
new file mode 100644
index 0000000..88bc1f8
--- /dev/null
+++ b/examples/run_controller_with_scanner.py
@@ -0,0 +1,74 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import logging
+import asyncio
+import sys
+import os
+from colors import color
+
+from bumble.device import Device
+from bumble.controller import Controller
+from bumble.link import LocalLink
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+class ScannerListener(Device.Listener):
+    def on_advertisement(self, address, ad_data, rssi, connectable):
+        address_type_string = ('P', 'R', 'PI', 'RI')[address.address_type]
+        address_color = 'yellow' if connectable else 'red'
+        if address_type_string.startswith('P'):
+            type_color = 'green'
+        else:
+            type_color = 'cyan'
+
+        print(f'>>> {color(address, address_color)} [{color(address_type_string, type_color)}]: RSSI={rssi}, {ad_data}')
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) != 2:
+        print('Usage: run_controller.py <transport-spec>')
+        print('example: run_controller_with_scanner.py serial:/dev/pts/14,1000000')
+        return
+
+    print('>>> connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[1]) as (hci_source, hci_sink):
+        print('>>> connected')
+
+        # Create a local link
+        link = LocalLink()
+
+        # Create a first controller using the packet source/sink as its host interface
+        controller1 = Controller('C1', host_source = hci_source, host_sink = hci_sink, link = link)
+        controller1.address = 'E0:E1:E2:E3:E4:E5'
+
+        # Create a second controller using the same link
+        controller2 = Controller('C2', link = link)
+
+        # Create a device with a scanner listener
+        device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', controller2, controller2)
+        device.listener = ScannerListener()
+        await device.power_on()
+        await device.start_scanning()
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_gatt_client.py b/examples/run_gatt_client.py
new file mode 100644
index 0000000..5af86fb
--- /dev/null
+++ b/examples/run_gatt_client.py
@@ -0,0 +1,98 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+from colors import color
+
+from bumble.core import ProtocolError
+from bumble.device import Device, Peer
+from bumble.gatt import show_services
+from bumble.transport import open_transport_or_link
+from bumble.utils import AsyncRunner
+
+
+# -----------------------------------------------------------------------------
+class Listener(Device.Listener):
+    def __init__(self, device):
+        self.device = device
+
+    @AsyncRunner.run_in_task()
+    async def on_connection(self, connection):
+        print(f'=== Connected to {connection}')
+
+        # Discover all services
+        print('=== Discovering services')
+        peer = Peer(connection)
+        await peer.discover_services()
+        for service in peer.services:
+            await service.discover_characteristics()
+            for characteristic in service.characteristics:
+                await characteristic.discover_descriptors()
+
+        print('=== Services discovered')
+        show_services(peer.services)
+
+        # Discover all attributes
+        print('=== Discovering attributes')
+        attributes = await peer.discover_attributes()
+        for attribute in attributes:
+            print(attribute)
+        print('=== Attributes discovered')
+
+        # Read all attributes
+        for attribute in attributes:
+            try:
+                value = await peer.read_value(attribute)
+                print(color(f'0x{attribute.handle:04X} = {value.hex()}', 'green'))
+            except ProtocolError as error:
+                print(color(f'cannot read {attribute.handle:04X}:', 'red'), error)
+            except TimeoutError:
+                print(color('read timeout'))
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_gatt_client.py <device-config> <transport-spec> [<bluetooth-address>]')
+        print('example: run_gatt_client.py device1.json usb:0 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device to manage the host, with a custom listener
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.listener = Listener(device)
+        await device.power_on()
+
+        # Connect to a peer
+        if len(sys.argv) > 3:
+            target_address = sys.argv[3]
+            print(f'=== Connecting to {target_address}...')
+            await device.connect(target_address)
+        else:
+            await device.start_advertising()
+
+        await asyncio.get_running_loop().create_future()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_gatt_client_and_server.py b/examples/run_gatt_client_and_server.py
new file mode 100644
index 0000000..940b1a8
--- /dev/null
+++ b/examples/run_gatt_client_and_server.py
@@ -0,0 +1,114 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import os
+import logging
+from colors import color
+
+from bumble.core import ProtocolError
+from bumble.controller import Controller
+from bumble.device import Device, Peer
+from bumble.host import Host
+from bumble.link import LocalLink
+from bumble.gatt import (
+    Service,
+    Characteristic,
+    Descriptor,
+    show_services,
+    GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR,
+    GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+    GATT_DEVICE_INFORMATION_SERVICE
+)
+
+
+# -----------------------------------------------------------------------------
+class ServerListener(Device.Listener):
+    def on_connection(self, connection):
+        print(f'### Server:  connected to {connection}')
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    # Create a local link
+    link = LocalLink()
+
+    # Setup a stack for the client
+    client_controller = Controller("client controller", link = link)
+    client_host = Host()
+    client_host.controller = client_controller
+    client_device = Device("client", address = 'F0:F1:F2:F3:F4:F5', host = client_host)
+    await client_device.power_on()
+
+    # Setup a stack for the server
+    server_controller = Controller("server controller", link = link)
+    server_host = Host()
+    server_host.controller = server_controller
+    server_device = Device("server", address = 'F6:F7:F8:F9:FA:FB', host = server_host)
+    server_device.listener = ServerListener()
+    await server_device.power_on()
+
+    # Add a few entries to the device's GATT server
+    descriptor = Descriptor(GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, Descriptor.READABLE, 'My Description')
+    manufacturer_name_characteristic = Characteristic(
+        GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+        Characteristic.READ,
+        Characteristic.READABLE,
+        "Fitbit",
+        [descriptor]
+    )
+    device_info_service = Service(GATT_DEVICE_INFORMATION_SERVICE, [
+        manufacturer_name_characteristic
+    ])
+    server_device.add_service(device_info_service)
+
+    # Connect the client to the server
+    connection = await client_device.connect(server_device.random_address)
+    print(f'=== Client: connected to {connection}')
+
+    # Discover all services
+    print('=== Discovering services')
+    peer = Peer(connection)
+    await peer.discover_services()
+    for service in peer.services:
+        await service.discover_characteristics()
+        for characteristic in service.characteristics:
+            await characteristic.discover_descriptors()
+
+    print('=== Services discovered')
+    show_services(peer.services)
+
+    # Discover all attributes
+    print('=== Discovering attributes')
+    attributes = await peer.discover_attributes()
+    for attribute in attributes:
+        print(attribute)
+    print('=== Attributes discovered')
+
+    # Read all attributes
+    for attribute in attributes:
+        try:
+            value = await attribute.read_value()
+            print(color(f'0x{attribute.handle:04X} = {value.hex()}', 'green'))
+        except ProtocolError as error:
+            print(color(f'cannot read {attribute.handle:04X}:', 'red'), error)
+
+    await asyncio.get_running_loop().create_future()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_gatt_server.py b/examples/run_gatt_server.py
new file mode 100644
index 0000000..099d589
--- /dev/null
+++ b/examples/run_gatt_server.py
@@ -0,0 +1,147 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from bumble.device import Device, Connection
+from bumble.transport import open_transport_or_link
+from bumble.att import (
+    ATT_Error,
+    ATT_INSUFFICIENT_ENCRYPTION_ERROR
+)
+from bumble.gatt import (
+    Service,
+    Characteristic,
+    CharacteristicValue,
+    Descriptor,
+    GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR,
+    GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+    GATT_DEVICE_INFORMATION_SERVICE
+)
+
+
+# -----------------------------------------------------------------------------
+class Listener(Device.Listener, Connection.Listener):
+    def __init__(self, device):
+        self.device = device
+
+    def on_connection(self, connection):
+        print(f'=== Connected to {connection}')
+        connection.listener = self
+
+    def on_disconnection(self, reason):
+        print(f'### Disconnected, reason={reason}')
+
+
+def my_custom_read(connection):
+    print('----- READ from', connection)
+    return bytes(f'Hello {connection}', 'ascii')
+
+
+def my_custom_write(connection, value):
+    print(f'----- WRITE from {connection}: {value}')
+
+
+def my_custom_read_with_error(connection):
+    print('----- READ from', connection, '[returning error]')
+    if connection.is_encrypted:
+        return bytes([123])
+    else:
+        raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR)
+
+
+def my_custom_write_with_error(connection, value):
+    print(f'----- WRITE from {connection}: {value}', '[returning error]')
+    if not connection.is_encrypted:
+        raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR)
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_gatt_server.py <device-config> <transport-spec> [<bluetooth-address>]')
+        print('example: run_gatt_server.py device1.json usb:0 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device to manage the host
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.listener = Listener(device)
+
+        # Add a few entries to the device's GATT server
+        descriptor = Descriptor(GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, Descriptor.READABLE, 'My Description')
+        manufacturer_name_characteristic = Characteristic(
+            GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
+            Characteristic.READ,
+            Characteristic.READABLE,
+            'Fitbit',
+            [descriptor]
+        )
+        device_info_service = Service(GATT_DEVICE_INFORMATION_SERVICE, [
+            manufacturer_name_characteristic
+        ])
+        custom_service1 = Service(
+            '50DB505C-8AC4-4738-8448-3B1D9CC09CC5',
+            [
+                Characteristic(
+                    'D901B45B-4916-412E-ACCA-376ECB603B2C',
+                    Characteristic.READ | Characteristic.WRITE,
+                    Characteristic.READABLE | Characteristic.WRITEABLE,
+                    CharacteristicValue(read=my_custom_read, write=my_custom_write)
+                ),
+                Characteristic(
+                    '552957FB-CF1F-4A31-9535-E78847E1A714',
+                    Characteristic.READ | Characteristic.WRITE,
+                    Characteristic.READABLE | Characteristic.WRITEABLE,
+                    CharacteristicValue(read=my_custom_read_with_error, write=my_custom_write_with_error)
+                ),
+                Characteristic(
+                    '486F64C6-4B5F-4B3B-8AFF-EDE134A8446A',
+                    Characteristic.READ | Characteristic.NOTIFY,
+                    Characteristic.READABLE,
+                    'hello'
+                )
+            ]
+        )
+        device.add_services([device_info_service, custom_service1])
+
+        # Debug print
+        for attribute in device.gatt_server.attributes:
+            print(attribute)
+
+        # Get things going
+        await device.power_on()
+
+        # Connect to a peer
+        if len(sys.argv) > 3:
+            target_address = sys.argv[3]
+            print(f'=== Connecting to {target_address}...')
+            await device.connect(target_address)
+        else:
+            await device.start_advertising(auto_restart=True)
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_hfp_gateway.py b/examples/run_hfp_gateway.py
new file mode 100644
index 0000000..69db0c1
--- /dev/null
+++ b/examples/run_hfp_gateway.py
@@ -0,0 +1,209 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from colors import color
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import ConnectionError, BT_BR_EDR_TRANSPORT
+from bumble.rfcomm import Client
+from bumble.sdp import (
+    Client as SDP_Client,
+    DataElement,
+    ServiceAttribute,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+)
+from bumble.hci import (
+    BT_HANDSFREE_SERVICE,
+    BT_RFCOMM_PROTOCOL_ID
+)
+from bumble.hfp import HfpProtocol
+
+
+# -----------------------------------------------------------------------------
+async def list_rfcomm_channels(device, connection):
+    # Connect to the SDP Server
+    sdp_client = SDP_Client(device)
+    await sdp_client.connect(connection)
+
+    # Search for services that support the Handsfree Profile
+    search_result = await sdp_client.search_attributes(
+        [BT_HANDSFREE_SERVICE],
+        [
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+        ]
+    )
+    print(color('==================================', 'blue'))
+    print(color('Handsfree Services:', 'yellow'))
+    rfcomm_channels = []
+    for attribute_list in search_result:
+        # Look for the RFCOMM Channel number
+        protocol_descriptor_list = ServiceAttribute.find_attribute_in_list(
+            attribute_list,
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID
+        )
+        if protocol_descriptor_list:
+            for protocol_descriptor in protocol_descriptor_list.value:
+                if len(protocol_descriptor.value) >= 2:
+                    if protocol_descriptor.value[0].value == BT_RFCOMM_PROTOCOL_ID:
+                        print(color('SERVICE:', 'green'))
+                        print(color('  RFCOMM Channel:', 'cyan'), protocol_descriptor.value[1].value)
+                        rfcomm_channels.append(protocol_descriptor.value[1].value)
+
+                        # List profiles
+                        bluetooth_profile_descriptor_list = ServiceAttribute.find_attribute_in_list(
+                            attribute_list,
+                            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+                        )
+                        if bluetooth_profile_descriptor_list:
+                            if bluetooth_profile_descriptor_list.value:
+                                if bluetooth_profile_descriptor_list.value[0].type == DataElement.SEQUENCE:
+                                    bluetooth_profile_descriptors = bluetooth_profile_descriptor_list.value
+                                else:
+                                    # Sometimes, instead of a list of lists, we just find a list. Fix that
+                                    bluetooth_profile_descriptors = [bluetooth_profile_descriptor_list]
+
+                                print(color('  Profiles:', 'green'))
+                                for bluetooth_profile_descriptor in bluetooth_profile_descriptors:
+                                    version_major = bluetooth_profile_descriptor.value[1].value >> 8
+                                    version_minor = bluetooth_profile_descriptor.value[1].value & 0xFF
+                                    print(f'    {bluetooth_profile_descriptor.value[0].value} - version {version_major}.{version_minor}')
+
+                        # List service classes
+                        service_class_id_list = ServiceAttribute.find_attribute_in_list(
+                            attribute_list,
+                            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+                        )
+                        if service_class_id_list:
+                            if service_class_id_list.value:
+                                print(color('  Service Classes:', 'green'))
+                                for service_class_id in service_class_id_list.value:
+                                    print('   ', service_class_id.value)
+
+    await sdp_client.disconnect()
+    return rfcomm_channels
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 4:
+        print('Usage: run_hfp_gateway.py <device-config> <transport-spec> <bluetooth-address>')
+        print('  specifying a channel number, or "discover" to list all RFCOMM channels')
+        print('example: run_hfp_gateway.py hfp_gateway.json usb:04b4:f901 E1:CA:72:48:C4:E8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+        await device.power_on()
+
+        # Connect to a peer
+        target_address = sys.argv[3]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+        print(f'=== Connected to {connection.peer_address}!')
+
+        # Get a list of all the Handsfree services (should only be 1)
+        channels = await list_rfcomm_channels(device, connection)
+        if len(channels) == 0:
+            print('!!! no service found')
+            return
+
+        # Pick the first one
+        channel = channels[0]
+
+        # Request authentication
+        print('*** Authenticating...')
+        await connection.authenticate()
+        print('*** Authenticated')
+
+        # Enable encryption
+        print('*** Enabling encryption...')
+        await connection.encrypt()
+        print('*** Encryption on')
+
+        # Create a client and start it
+        print('@@@ Starting to RFCOMM client...')
+        rfcomm_client = Client(device, connection)
+        rfcomm_mux = await rfcomm_client.start()
+        print('@@@ Started')
+
+        print(f'### Opening session for channel {channel}...')
+        try:
+            session = await rfcomm_mux.open_dlc(channel)
+            print('### Session open', session)
+        except ConnectionError as error:
+            print(f'### Session open failed: {error}')
+            await rfcomm_mux.disconnect()
+            print('@@@ Disconnected from RFCOMM server')
+            return
+
+        # Protocol loop (just for testing at this point)
+        protocol = HfpProtocol(session)
+        while True:
+            line = await protocol.next_line()
+
+            if line.startswith('AT+BRSF='):
+                protocol.send_response_line('+BRSF: 30')
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+CIND=?'):
+                protocol.send_response_line('+CIND: ("call",(0,1)),("callsetup",(0-3)),("service",(0-1)),("signal",(0-5)),("roam",(0,1)),("battchg",(0-5)),("callheld",(0-2))')
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+CIND?'):
+                protocol.send_response_line('+CIND: 0,0,1,4,1,5,0')
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+CMER='):
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+CHLD=?'):
+                protocol.send_response_line('+CHLD: 0')
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+BTRH?'):
+                protocol.send_response_line('+BTRH: 0')
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+CLIP='):
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+VGS='):
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+BIA='):
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+BVRA='):
+                protocol.send_response_line('+BVRA: 1,1,12AA,1,1,"Message 1 from Janina"')
+            elif line.startswith('AT+XEVENT='):
+                protocol.send_response_line('OK')
+            elif line.startswith('AT+XAPL='):
+                protocol.send_response_line('OK')
+            else:
+                print(color('UNSUPPORTED AT COMMAND', 'red'))
+                protocol.send_response_line('ERROR')
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_hfp_handsfree.py b/examples/run_hfp_handsfree.py
new file mode 100644
index 0000000..cf7a053
--- /dev/null
+++ b/examples/run_hfp_handsfree.py
@@ -0,0 +1,165 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+import websockets
+import json
+
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.rfcomm import Server as RfommServer
+from bumble.sdp import (
+    DataElement,
+    ServiceAttribute,
+    SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+)
+from bumble.core import (
+    BT_GENERIC_AUDIO_SERVICE,
+    BT_HANDSFREE_SERVICE,
+    BT_L2CAP_PROTOCOL_ID,
+    BT_RFCOMM_PROTOCOL_ID
+)
+from bumble.hfp import HfpProtocol
+
+
+# -----------------------------------------------------------------------------
+def make_sdp_records(rfcomm_channel):
+    return {
+        0x00010001: [
+            ServiceAttribute(
+                SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+                DataElement.unsigned_integer_32(0x00010001)
+            ),
+            ServiceAttribute(
+                SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+                DataElement.sequence([
+                    DataElement.uuid(BT_HANDSFREE_SERVICE),
+                    DataElement.uuid(BT_GENERIC_AUDIO_SERVICE)
+                ])
+            ),
+            ServiceAttribute(
+                SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+                DataElement.sequence([
+                    DataElement.sequence([
+                        DataElement.uuid(BT_L2CAP_PROTOCOL_ID)
+                    ]),
+                    DataElement.sequence([
+                        DataElement.uuid(BT_RFCOMM_PROTOCOL_ID),
+                        DataElement.unsigned_integer_8(rfcomm_channel)
+                    ])
+                ])
+            ),
+            ServiceAttribute(
+                SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+                DataElement.sequence([
+                    DataElement.sequence([
+                        DataElement.uuid(BT_HANDSFREE_SERVICE),
+                        DataElement.unsigned_integer_16(0x0105)
+                    ])
+                ])
+            )
+        ]
+    }
+
+
+# -----------------------------------------------------------------------------
+class UiServer:
+    protocol = None
+
+    async def start(self):
+        # Start a Websocket server to receive events from a web page
+        async def serve(websocket, path):
+            while True:
+                try:
+                    message = await websocket.recv()
+                    print('Received: ', str(message))
+
+                    parsed = json.loads(message)
+                    message_type = parsed['type']
+                    if message_type == 'at_command':
+                        if self.protocol is not None:
+                            self.protocol.send_command_line(parsed['command'])
+
+                except websockets.exceptions.ConnectionClosedOK:
+                    pass
+        await websockets.serve(serve, 'localhost', 8989)
+
+
+# -----------------------------------------------------------------------------
+async def protocol_loop(protocol):
+    await protocol.initialize_service()
+
+    while True:
+        await(protocol.next_line())
+
+
+# -----------------------------------------------------------------------------
+def on_dlc(dlc):
+    print('*** DLC connected', dlc)
+    protocol = HfpProtocol(dlc)
+    UiServer.protocol = protocol
+    asyncio.create_task(protocol_loop(protocol))
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_classic_hfp.py <device-config> <transport-spec>')
+        print('example: run_classic_hfp.py classic2.json usb:04b4:f901')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+
+        # Create and register a server
+        rfcomm_server = RfommServer(device)
+
+        # Listen for incoming DLC connections
+        channel_number = rfcomm_server.listen(on_dlc)
+        print(f'### Listening for connection on channel {channel_number}')
+
+        # Advertise the HFP RFComm channel in the SDP
+        device.sdp_service_records = make_sdp_records(channel_number)
+
+        # Let's go!
+        await device.power_on()
+
+        # Start being discoverable and connectable
+        await device.set_discoverable(True)
+        await device.set_connectable(True)
+
+        # Start the UI websocket server to offer a few buttons and input boxes
+        ui_server = UiServer()
+        await ui_server.start()
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_notifier.py b/examples/run_notifier.py
new file mode 100644
index 0000000..f52b56c
--- /dev/null
+++ b/examples/run_notifier.py
@@ -0,0 +1,120 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import random
+import logging
+
+from bumble.device import Device, Connection
+from bumble.transport import open_transport_or_link
+from bumble.gatt import (
+    Service,
+    Characteristic
+)
+
+
+# -----------------------------------------------------------------------------
+class Listener(Device.Listener, Connection.Listener):
+    def __init__(self, device):
+        self.device = device
+
+    def on_connection(self, connection):
+        print(f'=== Connected to {connection}')
+        connection.listener = self
+
+    def on_disconnection(self, reason):
+        print(f'### Disconnected, reason={reason}')
+
+    def on_characteristic_subscription(self, connection, characteristic, notify_enabled, indicate_enabled):
+        print(
+            f'$$$ Characteristic subscription for handle {characteristic.handle} from {connection}: '
+            f'notify {"enabled" if notify_enabled else "disabled"}, '
+            f'indicate {"enabled" if indicate_enabled else "disabled"}'
+        )
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_gatt_server.py <device-config> <transport-spec>')
+        print('example: run_gatt_server.py device1.json usb:0')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device to manage the host
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.listener = Listener(device)
+
+        # Add a few entries to the device's GATT server
+        characteristic1 = Characteristic(
+            '486F64C6-4B5F-4B3B-8AFF-EDE134A8446A',
+            Characteristic.READ | Characteristic.NOTIFY,
+            Characteristic.READABLE,
+            bytes([0x40])
+        )
+        characteristic2 = Characteristic(
+            '8EBDEBAE-0017-418E-8D3B-3A3809492165',
+            Characteristic.READ | Characteristic.INDICATE,
+            Characteristic.READABLE,
+            bytes([0x41])
+        )
+        characteristic3 = Characteristic(
+            '8EBDEBAE-0017-418E-8D3B-3A3809492165',
+            Characteristic.READ | Characteristic.NOTIFY | Characteristic.INDICATE,
+            Characteristic.READABLE,
+            bytes([0x42])
+        )
+        custom_service = Service(
+            '50DB505C-8AC4-4738-8448-3B1D9CC09CC5',
+            [characteristic1, characteristic2, characteristic3]
+        )
+        device.add_services([custom_service])
+
+        # Debug print
+        for attribute in device.gatt_server.attributes:
+            print(attribute)
+
+        # Get things going
+        await device.power_on()
+
+        # Connect to a peer
+        if len(sys.argv) > 3:
+            target_address = sys.argv[3]
+            print(f'=== Connecting to {target_address}...')
+            await device.connect(target_address)
+        else:
+            await device.start_advertising(auto_restart=True)
+
+        while True:
+            await asyncio.sleep(3.0)
+            characteristic1.value = bytes([random.randint(0, 255)])
+            await device.notify_subscribers(characteristic1)
+            characteristic2.value = bytes([random.randint(0, 255)])
+            await device.indicate_subscribers(characteristic2)
+            characteristic3.value = bytes([random.randint(0, 255)])
+            await device.notify_subscribers(characteristic3)
+            await device.indicate_subscribers(characteristic3)
+
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_rfcomm_client.py b/examples/run_rfcomm_client.py
new file mode 100644
index 0000000..83ef848
--- /dev/null
+++ b/examples/run_rfcomm_client.py
@@ -0,0 +1,201 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from colors import color
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import ConnectionError, BT_BR_EDR_TRANSPORT
+from bumble.rfcomm import Client
+from bumble.sdp import (
+    Client as SDP_Client,
+    DataElement,
+    ServiceAttribute,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+)
+from bumble.hci import BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID
+
+
+# -----------------------------------------------------------------------------
+async def list_rfcomm_channels(device, connection):
+    # Connect to the SDP Server
+    sdp_client = SDP_Client(device)
+    await sdp_client.connect(connection)
+
+    # Search for services with an L2CAP service attribute
+    search_result = await sdp_client.search_attributes(
+        [BT_L2CAP_PROTOCOL_ID],
+        [
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+        ]
+    )
+    print(color('==================================', 'blue'))
+    print(color('RFCOMM Services:', 'yellow'))
+    for attribute_list in search_result:
+        # Look for the RFCOMM Channel number
+        protocol_descriptor_list = ServiceAttribute.find_attribute_in_list(
+            attribute_list,
+            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID
+        )
+        if protocol_descriptor_list:
+            for protocol_descriptor in protocol_descriptor_list.value:
+                if len(protocol_descriptor.value) >= 2:
+                    if protocol_descriptor.value[0].value == BT_RFCOMM_PROTOCOL_ID:
+                        print(color('SERVICE:', 'green'))
+                        print(color('  RFCOMM Channel:', 'cyan'), protocol_descriptor.value[1].value)
+
+                        # List profiles
+                        bluetooth_profile_descriptor_list = ServiceAttribute.find_attribute_in_list(
+                            attribute_list,
+                            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID
+                        )
+                        if bluetooth_profile_descriptor_list:
+                            if bluetooth_profile_descriptor_list.value:
+                                if bluetooth_profile_descriptor_list.value[0].type == DataElement.SEQUENCE:
+                                    bluetooth_profile_descriptors = bluetooth_profile_descriptor_list.value
+                                else:
+                                    # Sometimes, instead of a list of lists, we just find a list. Fix that
+                                    bluetooth_profile_descriptors = [bluetooth_profile_descriptor_list]
+
+                                print(color('  Profiles:', 'green'))
+                                for bluetooth_profile_descriptor in bluetooth_profile_descriptors:
+                                    version_major = bluetooth_profile_descriptor.value[1].value >> 8
+                                    version_minor = bluetooth_profile_descriptor.value[1].value & 0xFF
+                                    print(f'    {bluetooth_profile_descriptor.value[0].value} - version {version_major}.{version_minor}')
+
+                        # List service classes
+                        service_class_id_list = ServiceAttribute.find_attribute_in_list(
+                            attribute_list,
+                            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID
+                        )
+                        if service_class_id_list:
+                            if service_class_id_list.value:
+                                print(color('  Service Classes:', 'green'))
+                                for service_class_id in service_class_id_list.value:
+                                    print('   ', service_class_id.value)
+
+    await sdp_client.disconnect()
+
+# -----------------------------------------------------------------------------
+class TcpServerProtocol(asyncio.Protocol):
+    def __init__(self, rfcomm_session):
+        self.rfcomm_session = rfcomm_session
+
+    def connection_made(self, transport):
+        peername = transport.get_extra_info('peername')
+        print(f'<<< TCP Server: connection from {peername}')
+        self.transport = transport
+        self.rfcomm_session.sink = self.rfcomm_data_received
+
+    def rfcomm_data_received(self, data):
+        print(f'<<< RFCOMM Data: {data.hex()}')
+        if self.transport:
+            self.transport.write(data)
+        else:
+            print('!!! no TCP connection, dropping data')
+
+    def data_received(self, data):
+        print(f'<<< TCP Server: data received: {len(data)} bytes - {data.hex()}')
+        self.rfcomm_session.write(data)
+
+
+# -----------------------------------------------------------------------------
+async def tcp_server(tcp_port, rfcomm_session):
+    print(f'$$$ Starting TCP server on port {tcp_port}')
+
+    server = await asyncio.get_running_loop().create_server(
+        lambda: TcpServerProtocol(rfcomm_session), '127.0.0.1', tcp_port
+    )
+    await asyncio.get_running_loop().create_future()
+
+    async with server:
+        await server.serve_forever()
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 5:
+        print('Usage: run_rfcomm_client.py <device-config> <transport-spec> <bluetooth-address> <channel>|discover [tcp-port]')
+        print('  specifying a channel number, or "discover" to list all RFCOMM channels')
+        print('example: run_rfcomm_client.py classic1.json usb:04b4:f901 E1:CA:72:48:C4:E8 8')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+        await device.power_on()
+
+        # Connect to a peer
+        target_address = sys.argv[3]
+        print(f'=== Connecting to {target_address}...')
+        connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+        print(f'=== Connected to {connection.peer_address}!')
+
+        channel = sys.argv[4]
+        if channel == 'discover':
+            await list_rfcomm_channels(device, connection)
+            return
+
+        # Request authentication
+        print('*** Authenticating...')
+        await connection.authenticate()
+        print('*** Authenticated')
+
+        # Enable encryption
+        print('*** Enabling encryption...')
+        await connection.encrypt()
+        print('*** Encryption on')
+
+        # Create a client and start it
+        print('@@@ Starting to RFCOMM client...')
+        rfcomm_client = Client(device, connection)
+        rfcomm_mux = await rfcomm_client.start()
+        print('@@@ Started')
+
+        channel = int(channel)
+        print(f'### Opening session for channel {channel}...')
+        try:
+            session = await rfcomm_mux.open_dlc(channel)
+            print('### Session open', session)
+        except ConnectionError as error:
+            print(f'### Session open failed: {error}')
+            await rfcomm_mux.disconnect()
+            print('@@@ Disconnected from RFCOMM server')
+            return
+
+        if len(sys.argv) == 6:
+            # A TCP port was specified, start listening
+            tcp_port = int(sys.argv[5])
+            asyncio.get_running_loop().create_task(tcp_server(tcp_port, session))
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_rfcomm_server.py b/examples/run_rfcomm_server.py
new file mode 100644
index 0000000..a239ceb
--- /dev/null
+++ b/examples/run_rfcomm_server.py
@@ -0,0 +1,118 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import UUID
+from bumble.rfcomm import Server
+from bumble.sdp import (
+    DataElement,
+    ServiceAttribute,
+    SDP_PUBLIC_BROWSE_ROOT,
+    SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+    SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
+    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID
+)
+from bumble.hci import BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID
+
+
+# -----------------------------------------------------------------------------
+def sdp_records(channel):
+    return {
+        0x00010001: [
+            ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(0x00010001)),
+            ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([
+                DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)
+            ])),
+            ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([
+                DataElement.uuid(UUID('E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'))
+            ])),
+            ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([
+                DataElement.sequence([
+                    DataElement.uuid(BT_L2CAP_PROTOCOL_ID)
+                ]),
+                DataElement.sequence([
+                    DataElement.uuid(BT_RFCOMM_PROTOCOL_ID),
+                    DataElement.unsigned_integer_8(channel)
+                ])
+            ]))
+        ]
+    }
+
+
+# -----------------------------------------------------------------------------
+def on_dlc(dlc):
+    print('*** DLC connected', dlc)
+    dlc.sink = lambda data: on_rfcomm_data_received(dlc, data)
+
+
+# -----------------------------------------------------------------------------
+def on_rfcomm_data_received(dlc, data):
+    print(f'<<< Data received: {data.hex()}')
+    try:
+        message = data.decode('utf-8')
+        print(f'<<< Message = {message}')
+    except Exception:
+        pass
+
+    # Echo everything back
+    dlc.write(data)
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 3:
+        print('Usage: run_rfcomm_server.py <device-config> <transport-spec>')
+        print('example: run_rfcomm_server.py classic2.json usb:04b4:f901')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+        print('<<< connected')
+
+        # Create a device
+        device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+        device.classic_enabled = True
+
+        # Create and register a server
+        rfcomm_server = Server(device)
+
+        # Listen for incoming DLC connections
+        channel_number = rfcomm_server.listen(on_dlc)
+        print(f'### Listening for connection on channel {channel_number}')
+
+        # Setup the SDP to advertise this channel
+        device.sdp_service_records = sdp_records(channel_number)
+
+        # Start the controller
+        await device.power_on()
+
+        # Start being discoverable and connectable
+        await device.set_discoverable(True)
+        await device.set_connectable(True)
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/examples/run_scanner.py b/examples/run_scanner.py
new file mode 100644
index 0000000..feed88f
--- /dev/null
+++ b/examples/run_scanner.py
@@ -0,0 +1,69 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+from colors import color
+
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+
+
+# -----------------------------------------------------------------------------
+async def main():
+    if len(sys.argv) < 2:
+        print('Usage: run_scanner.py <transport-spec> [filter]')
+        print('example: run_scanner.py usb:0')
+        return
+
+    print('<<< connecting to HCI...')
+    async with await open_transport_or_link(sys.argv[1]) as (hci_source, hci_sink):
+        print('<<< connected')
+        filter_duplicates = (len(sys.argv) == 3 and sys.argv[2] == 'filter')
+
+        device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+
+        @device.on('advertisement')
+        def _(address, ad_data, rssi, connectable):
+            address_type_string = ('PUBLIC', 'RANDOM', 'PUBLIC_ID', 'RANDOM_ID')[address.address_type]
+            address_color = 'yellow' if connectable else 'red'
+            address_qualifier = ''
+            if address_type_string.startswith('P'):
+                type_color = 'cyan'
+            else:
+                if address.is_static:
+                    type_color = 'green'
+                    address_qualifier = '(static)'
+                elif address.is_resolvable:
+                    type_color = 'magenta'
+                    address_qualifier = '(resolvable)'
+                else:
+                    type_color = 'white'
+
+            separator = '\n  '
+            print(f'>>> {color(address, address_color)} [{color(address_type_string, type_color)}]{address_qualifier}:{separator}RSSI:{rssi}{separator}{ad_data.to_string(separator)}')
+
+        await device.power_on()
+        await device.start_scanning(filter_duplicates=filter_duplicates)
+
+        await hci_source.wait_for_termination()
+
+# -----------------------------------------------------------------------------
+logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/noxfile.py b/noxfile.py
new file mode 100644
index 0000000..a45e88c
--- /dev/null
+++ b/noxfile.py
@@ -0,0 +1,21 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import nox
+
+
+@nox.session
+def tests(session):
+    session.install('.[test]')
+    session.run('pytest', *session.posargs)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..6eca00c
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,5 @@
+[build-system]
+requires = ["setuptools>=52", "wheel", "setuptools_scm>=6.2"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools_scm]
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..1711d7c
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,68 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[metadata]
+name = bumble
+use_scm_version = True
+description = Bluetooth Stack for Apps, Emulation, Test and Experimentation
+author = Google
+author_email = tbd@tbd.com
+url = https://github.com/google/bumble
+
+[options]
+python_requires = >=3.8
+packages = bumble, bumble.transport, bumble.apps, bumble.apps.link_relay
+package_dir =
+    bumble = bumble
+    bumble.apps = apps
+install_requires =
+    aioconsole >= 0.4.1
+    ansicolors >= 1.1
+    appdirs >= 1.4
+    bitstruct >= 8.12
+    click >= 7.1.2; platform_system!='Emscripten'
+    cryptography == 35; platform_system!='Emscripten'
+    grpcio >= 1.46; platform_system!='Emscripten'
+    libusb1 >= 2.0.1; platform_system!='Emscripten'
+    prompt_toolkit >= 3.0.16; platform_system!='Emscripten'
+    protobuf >= 3.12.4
+    pyee >= 8.2.2
+    pyserial-asyncio >= 0.5; platform_system!='Emscripten'
+    pyserial >= 3.5; platform_system!='Emscripten'
+    pyusb >= 1.2; platform_system!='Emscripten'
+    websockets >= 8.1; platform_system!='Emscripten'
+
+[options.entry_points]
+console_scripts =
+    bumble-console = bumble.apps.console:main
+    bumble-gatt-dump = bumble.apps.gatt_dump:main
+    bumble-hci-bridge = bumble.apps.hci_bridge:main
+    bumble-pair = bumble.apps.pair:main
+    bumble-scan = bumble.apps.scan:main
+    bumble-show = bumble.apps.show:main
+    bumble-unbond = bumble.apps.unbond:main
+    bumble-link-relay = bumble.apps.link_relay.link_relay:main
+
+[options.extras_require]
+test =
+    pytest >= 6.2
+    pytest-asyncio >= 0.17
+development =
+    invoke >= 1.4
+    build >= 0.7
+    nox >= 2022
+documentation =
+    mkdocs >= 1.2.3
+    mkdocs-material >= 8.1.9
+    mkdocstrings[python] >= 0.19.0
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..16ecaca
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,16 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from setuptools import setup
+setup()
diff --git a/tasks.py b/tasks.py
new file mode 100644
index 0000000..c368291
--- /dev/null
+++ b/tasks.py
@@ -0,0 +1,57 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Invoke tasks
+"""
+import os
+
+from invoke import task, Collection
+
+ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+ns = Collection()
+
+build_tasks = Collection()
+ns.add_collection(build_tasks, name='build')
+
+
+@task
+def build(ctx):
+    ctx.run('python -m build')
+
+build_tasks.add_task(build, default=True, name='build')
+
+test_tasks = Collection()
+ns.add_collection(test_tasks, name='test')
+
+@task
+def test(ctx, filter=None, junit=False):
+    args = ""
+    if junit:
+        args += "--junit-xml test-results.xml"
+    if filter is not None:
+        args += " -k '{}'".format(filter)
+    ctx.run('python -m pytest {} {}'
+            .format(os.path.join(ROOT_DIR, "tests"), args))
+
+test_tasks.add_task(test, name='test', default=True)
+
+
+@task
+def mkdocs(ctx):
+    ctx.run('mkdocs build -f docs/mkdocs/mkdocs.yml')
+
+
+ns.add_task(mkdocs)
diff --git a/tests/a2dp_test.py b/tests/a2dp_test.py
new file mode 100644
index 0000000..e09694d
--- /dev/null
+++ b/tests/a2dp_test.py
@@ -0,0 +1,250 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import os
+import pytest
+
+from bumble.controller import Controller
+from bumble.link import LocalLink
+from bumble.device import Device
+from bumble.host import Host
+from bumble.transport import AsyncPipeSink
+from bumble.avdtp import (
+    AVDTP_IDLE_STATE,
+    AVDTP_STREAMING_STATE,
+    MediaPacketPump,
+    Protocol,
+    Listener,
+    MediaCodecCapabilities,
+    MediaPacket,
+    AVDTP_AUDIO_MEDIA_TYPE,
+    AVDTP_TSEP_SNK,
+    A2DP_SBC_CODEC_TYPE
+)
+from bumble.a2dp import (
+    SbcMediaCodecInformation,
+    SBC_MONO_CHANNEL_MODE,
+    SBC_DUAL_CHANNEL_MODE,
+    SBC_STEREO_CHANNEL_MODE,
+    SBC_JOINT_STEREO_CHANNEL_MODE,
+    SBC_LOUDNESS_ALLOCATION_METHOD,
+    SBC_SNR_ALLOCATION_METHOD
+)
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+class TwoDevices:
+    def __init__(self):
+        self.connections = [None, None]
+
+        self.link = LocalLink()
+        self.controllers = [
+            Controller('C1', link = self.link),
+            Controller('C2', link = self.link)
+        ]
+        self.devices = [
+            Device(
+                address = 'F0:F1:F2:F3:F4:F5',
+                host    = Host(self.controllers[0], AsyncPipeSink(self.controllers[0]))
+            ),
+            Device(
+                address = 'F5:F4:F3:F2:F1:F0',
+                host    = Host(self.controllers[1], AsyncPipeSink(self.controllers[1]))
+            )
+        ]
+
+        self.paired = [None, None]
+
+    def on_connection(self, which, connection):
+        self.connections[which] = connection
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_self_connection():
+    # Create two devices, each with a controller, attached to the same link
+    two_devices = TwoDevices()
+
+    # Attach listeners
+    two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection))
+    two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection))
+
+    # Start
+    await two_devices.devices[0].power_on()
+    await two_devices.devices[1].power_on()
+
+    # Connect the two devices
+    await two_devices.devices[0].connect(two_devices.devices[1].random_address)
+
+    # Check the post conditions
+    assert(two_devices.connections[0] is not None)
+    assert(two_devices.connections[1] is not None)
+
+
+# -----------------------------------------------------------------------------
+def source_codec_capabilities():
+    return MediaCodecCapabilities(
+        media_type                = AVDTP_AUDIO_MEDIA_TYPE,
+        media_codec_type          = A2DP_SBC_CODEC_TYPE,
+        media_codec_information   = SbcMediaCodecInformation.from_discrete_values(
+            sampling_frequency    = 44100,
+            channel_mode          = SBC_JOINT_STEREO_CHANNEL_MODE,
+            block_length          = 16,
+            subbands              = 8,
+            allocation_method     = SBC_LOUDNESS_ALLOCATION_METHOD,
+            minimum_bitpool_value = 2,
+            maximum_bitpool_value = 53
+        )
+    )
+
+
+# -----------------------------------------------------------------------------
+def sink_codec_capabilities():
+    return MediaCodecCapabilities(
+        media_type                = AVDTP_AUDIO_MEDIA_TYPE,
+        media_codec_type          = A2DP_SBC_CODEC_TYPE,
+        media_codec_information   = SbcMediaCodecInformation.from_lists(
+            sampling_frequencies  = [48000, 44100, 32000, 16000],
+            channel_modes         = [
+                SBC_MONO_CHANNEL_MODE,
+                SBC_DUAL_CHANNEL_MODE,
+                SBC_STEREO_CHANNEL_MODE,
+                SBC_JOINT_STEREO_CHANNEL_MODE
+            ],
+            block_lengths         = [4, 8, 12, 16],
+            subbands              = [4, 8],
+            allocation_methods    = [SBC_LOUDNESS_ALLOCATION_METHOD, SBC_SNR_ALLOCATION_METHOD],
+            minimum_bitpool_value = 2,
+            maximum_bitpool_value = 53
+        )
+    )
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_source_sink_1():
+    two_devices = TwoDevices()
+    await two_devices.devices[0].power_on()
+    await two_devices.devices[1].power_on()
+
+    def on_rtp_packet(packet):
+        rtp_packets.append(packet)
+        if len(rtp_packets) == rtp_packets_expected:
+            rtp_packets_fully_received.set_result(None)
+
+    sink = None
+
+    def on_avdtp_connection(server):
+        nonlocal sink
+        sink = server.add_sink(sink_codec_capabilities())
+        sink.on('rtp_packet', on_rtp_packet)
+
+    # Create a listener to wait for AVDTP connections
+    listener = Listener(Listener.create_registrar(two_devices.devices[1]))
+    listener.on('connection', on_avdtp_connection)
+
+    connection = await two_devices.devices[0].connect(two_devices.devices[1].random_address)
+    client = await Protocol.connect(connection)
+    endpoints = await client.discover_remote_endpoints()
+    assert(len(endpoints) == 1)
+    remote_sink = list(endpoints)[0]
+    assert(remote_sink.in_use == 0)
+    assert(remote_sink.media_type == AVDTP_AUDIO_MEDIA_TYPE)
+    assert(remote_sink.tsep == AVDTP_TSEP_SNK)
+
+    async def generate_packets(packet_count):
+        sequence_number = 0
+        timestamp = 0
+        for i in range(packet_count):
+            payload = bytes([sequence_number % 256])
+            packet = MediaPacket(2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, payload)
+            packet.timestamp_seconds = timestamp / 44100
+            timestamp += 10
+            sequence_number += 1
+            yield packet
+
+    # Send packets using a pump object
+    rtp_packets_fully_received = asyncio.get_running_loop().create_future()
+    rtp_packets_expected = 3
+    rtp_packets = []
+    pump = MediaPacketPump(generate_packets(3))
+    source = client.add_source(source_codec_capabilities(), pump)
+    stream = await client.create_stream(source, remote_sink)
+    await stream.start()
+    assert(stream.state == AVDTP_STREAMING_STATE)
+    assert(stream.local_endpoint.in_use == 1)
+    assert(stream.rtp_channel is not None)
+    assert(sink.in_use == 1)
+    assert(sink.stream is not None)
+    assert(sink.stream.state == AVDTP_STREAMING_STATE)
+    await rtp_packets_fully_received
+
+    await stream.close()
+    assert(stream.rtp_channel is None)
+    assert(source.in_use == 0)
+    assert(source.stream.state == AVDTP_IDLE_STATE)
+    assert(sink.in_use == 0)
+    assert(sink.stream.state == AVDTP_IDLE_STATE)
+
+    # Send packets manually
+    rtp_packets_fully_received = asyncio.get_running_loop().create_future()
+    rtp_packets_expected = 3
+    rtp_packets = []
+    source_packets = [
+        MediaPacket(2, 0, 0, 0, i, i * 10, 0, [], 96, bytes([i]))
+        for i in range(3)
+    ]
+    source = client.add_source(source_codec_capabilities(), None)
+    stream = await client.create_stream(source, remote_sink)
+    await stream.start()
+    assert(stream.state == AVDTP_STREAMING_STATE)
+    assert(stream.local_endpoint.in_use == 1)
+    assert(stream.rtp_channel is not None)
+    assert(sink.in_use == 1)
+    assert(sink.stream is not None)
+    assert(sink.stream.state == AVDTP_STREAMING_STATE)
+
+    stream.send_media_packet(source_packets[0])
+    stream.send_media_packet(source_packets[1])
+    stream.send_media_packet(source_packets[2])
+
+    await stream.close()
+    assert(stream.rtp_channel is None)
+    assert(len(rtp_packets) == 3)
+    assert(source.in_use == 0)
+    assert(source.stream.state == AVDTP_IDLE_STATE)
+    assert(sink.in_use == 0)
+    assert(sink.stream.state == AVDTP_IDLE_STATE)
+
+
+# -----------------------------------------------------------------------------
+async def run_test_self():
+    await test_self_connection()
+    await test_source_sink_1()
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(run_test_self())
diff --git a/tests/avdtp_test.py b/tests/avdtp_test.py
new file mode 100644
index 0000000..d14e4fa
--- /dev/null
+++ b/tests/avdtp_test.py
@@ -0,0 +1,65 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.a2dp import A2DP_SBC_CODEC_TYPE
+from bumble.avdtp import (
+    AVDTP_AUDIO_MEDIA_TYPE,
+    AVDTP_DELAY_REPORTING_SERVICE_CATEGORY,
+    AVDTP_GET_CAPABILITIES,
+    AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY,
+    AVDTP_SET_CONFIGURATION,
+    Message,
+    MediaPacket,
+    Get_Capabilities_Response,
+    Set_Configuration_Command,
+    Set_Configuration_Response,
+    ServiceCapabilities,
+    MediaCodecCapabilities
+)
+
+
+# -----------------------------------------------------------------------------
+def test_messages():
+    capabilities = [
+        ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
+        MediaCodecCapabilities(
+            media_type              = AVDTP_AUDIO_MEDIA_TYPE,
+            media_codec_type        = A2DP_SBC_CODEC_TYPE,
+            media_codec_information = bytes.fromhex('211502fa')
+        ),
+        ServiceCapabilities(AVDTP_DELAY_REPORTING_SERVICE_CATEGORY)
+    ]
+    message = Get_Capabilities_Response(capabilities)
+    parsed = Message.create(AVDTP_GET_CAPABILITIES, Message.RESPONSE_ACCEPT, message.payload)
+    assert(message.payload == parsed.payload)
+
+    message = Set_Configuration_Command(3, 4, capabilities)
+    parsed = Message.create(AVDTP_SET_CONFIGURATION, Message.COMMAND, message.payload)
+    assert(message.payload == parsed.payload)
+
+
+# -----------------------------------------------------------------------------
+def test_rtp():
+    packet = bytes.fromhex('8060000103141c6a000000000a9cbd2adbfe75443333542210037eeeed5f76dfbbbb57ddb890eed5f76e2ad3958613d3d04a5f596fc2b54d613a6a95570b4b49c2d0955ac710ca6abb293bb4580d5896b106cd6a7c4b557d8bb73aac56b8e633aa161447caa86585ae4cbc9576cc9cbd2a54fe7443322064221000b44a5cd51929bc96328916b1694e1f3611d6b6928dbf554b01e96d23a6ad879834d99326a649b94ca6adbeab1311e372a3aa3468e9582d2d9c857da28e5b76a2d363089367432930a0160af22d48911bc46cea549cbd2a03fe754332206532210054cf1d3d9260d3bc9895566f124b22c4b3cb6bc66648cf9b21e1613a48b3592466e90cee3424cc6cc56d2f569b12145234c6bd73560c95ad9c584c9d6c26552cea9905da55b3eab182c40e2dae64b46c328ba64d9cbd2a3cde74433220643211001e8d1ad6210d5c26b296d40d298a29b073b46bb4542ceb1aea011612c6df64c731068d49b56bb48afb2456ea9b5903222bb63b8b1a60c52896325a22aad781486cdb36269d9dc6dd38d9acf5b0e9328e0b23542c9cbd2adffe744323206432200095731b2a62604accea58da8ee6aba6d6fc9169ab66a824527412a66ac6c5c41d12c85295673c3263848c88ae934f62619c46ed2adccaaeb3eac70c396bb28cb8cecaf22423c548cd4adca92d30d1370ba34a772d9cbd2a3efe6442221064322100cc932cd12222dcd854d6da8d09330d2708b392a3997ec8a2f30b9312b8c562d9353513eda7733c4b835176eeca695909cc10d08614574d36cac669c583e68d9778daca9b92d6e4bb5cd008ef3562aa52332bc54a9cbd2a1efe6443332064322100a6e91a6ddc58a3a4b966a3452cb6d0b9c5334d2b695929128dcd6123b8b366d491122fd545f9b96cf769d530d2e2646b15c6a43695b12d33aa214e622e45b1ac132309a39eddc82caad35115b3d2350c5c6dcd749cbd2a9c7e654332207433110086ed5b68531a54c6e7bb052d15add1b204bd62568d8922d3379418b9c4e202482909ab712a744d81f392fa94193d62293ac6dfa7278f79b451c70c3b4b2b64d70f0b3463323c46f598ecd70d35e5a743282307099cbd2ae9fe654332106432110082acdb4aca734b843b6699f491ad3a511aab6db2344eeed386d0aa34c49c4b0a4b2aa59ec98bba6419b06310d2f9626c42a7466728f0ca0f1db579b46c0a701264e59153535228dc6497492dac722596138bd74a9cbd2a0b7e655432107432110056a8d22a62d643b428e513b52ea4a66c7a41991719370c8d9664ce2bca685dd2690b1c368c5dce36d26b38d10e0c672343ca8c25c58d0d5c568de433b7561c61268aaf83260b4b868dca8ee6dc6ba573abcb5093')
+    media_packet = MediaPacket.from_bytes(packet)
+    print(media_packet)
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    test_messages()
+    test_rtp()
\ No newline at end of file
diff --git a/tests/core_test.py b/tests/core_test.py
new file mode 100644
index 0000000..f4bdd83
--- /dev/null
+++ b/tests/core_test.py
@@ -0,0 +1,44 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.core import AdvertisingData
+
+
+# -----------------------------------------------------------------------------
+def test_ad_data():
+    data = bytes([2, AdvertisingData.TX_POWER_LEVEL, 123])
+    ad = AdvertisingData.from_bytes(data)
+    ad_bytes = bytes(ad)
+    assert(data == ad_bytes)
+    assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME) is None)
+    assert(ad.get(AdvertisingData.TX_POWER_LEVEL) == bytes([123]))
+    assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME, return_all=True) == [])
+    assert(ad.get(AdvertisingData.TX_POWER_LEVEL, return_all=True) == [bytes([123])])
+
+    data2 = bytes([2, AdvertisingData.TX_POWER_LEVEL, 234])
+    ad.append(data2)
+    ad_bytes = bytes(ad)
+    assert(ad_bytes == data + data2)
+    assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME) is None)
+    assert(ad.get(AdvertisingData.TX_POWER_LEVEL) == bytes([123]))
+    assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME, return_all=True) == [])
+    assert(ad.get(AdvertisingData.TX_POWER_LEVEL, return_all=True) == [bytes([123]), bytes([234])])
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    test_ad_data()
\ No newline at end of file
diff --git a/tests/gatt_test.py b/tests/gatt_test.py
new file mode 100644
index 0000000..5df6e08
--- /dev/null
+++ b/tests/gatt_test.py
@@ -0,0 +1,491 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import os
+import struct
+import pytest
+
+from bumble.controller import Controller
+from bumble.link import LocalLink
+from bumble.device import Device, Peer
+from bumble.host import Host
+from bumble.gatt import (
+    GATT_BATTERY_LEVEL_CHARACTERISTIC,
+    CharacteristicAdapter,
+    DelegatedCharacteristicAdapter,
+    PackedCharacteristicAdapter,
+    MappedCharacteristicAdapter,
+    UTF8CharacteristicAdapter,
+    Service,
+    Characteristic,
+    CharacteristicValue
+)
+from bumble.transport import AsyncPipeSink
+from bumble.core import UUID
+from bumble.att import (
+    ATT_EXCHANGE_MTU_REQUEST,
+    ATT_ATTRIBUTE_NOT_FOUND_ERROR,
+    ATT_PDU,
+    ATT_Error_Response,
+    ATT_Read_By_Group_Type_Request
+)
+
+
+# -----------------------------------------------------------------------------
+def basic_check(x):
+    pdu = x.to_bytes()
+    parsed = ATT_PDU.from_bytes(pdu)
+    x_str = str(x)
+    parsed_str = str(parsed)
+    assert(x_str == parsed_str)
+
+
+# -----------------------------------------------------------------------------
+def test_UUID():
+    u = UUID.from_16_bits(0x7788)
+    assert(str(u) == 'UUID-16:7788')
+    u = UUID.from_32_bits(0x11223344)
+    assert(str(u) == 'UUID-32:11223344')
+    u = UUID('61A3512C-09BE-4DDC-A6A6-0B03667AAFC6')
+    assert(str(u) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6')
+    v = UUID(str(u))
+    assert(str(v) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6')
+    w = UUID.from_bytes(v.to_bytes())
+    assert(str(w) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6')
+
+    u1 = UUID.from_16_bits(0x1234)
+    b1 = u1.to_bytes(force_128 = True)
+    u2 = UUID.from_bytes(b1)
+    assert(u1 == u2)
+
+    u3 = UUID.from_16_bits(0x180a)
+    assert(str(u3) == 'UUID-16:180A (Device Information)')
+
+
+# -----------------------------------------------------------------------------
+def test_ATT_Error_Response():
+    pdu = ATT_Error_Response(
+        request_opcode_in_error = ATT_EXCHANGE_MTU_REQUEST,
+        attribute_handle_in_error = 0x0000,
+        error_code = ATT_ATTRIBUTE_NOT_FOUND_ERROR
+    )
+    basic_check(pdu)
+
+
+# -----------------------------------------------------------------------------
+def test_ATT_Read_By_Group_Type_Request():
+    pdu = ATT_Read_By_Group_Type_Request(
+        starting_handle      = 0x0001,
+        ending_handle        = 0xFFFF,
+        attribute_group_type = UUID.from_16_bits(0x2800)
+    )
+    basic_check(pdu)
+
+
+# -----------------------------------------------------------------------------
+def test_CharacteristicAdapter():
+    # Check that the CharacteristicAdapter base class is transparent
+    v = bytes([1, 2, 3])
+    c = Characteristic(GATT_BATTERY_LEVEL_CHARACTERISTIC, Characteristic.READ, Characteristic.READABLE, v)
+    a = CharacteristicAdapter(c)
+
+    value = a.read_value(None)
+    assert(value == v)
+
+    v = bytes([3, 4, 5])
+    a.write_value(None, v)
+    assert(c.value == v)
+
+    # Simple delegated adapter
+    a = DelegatedCharacteristicAdapter(c, lambda x: bytes(reversed(x)), lambda x: bytes(reversed(x)))
+
+    value = a.read_value(None)
+    assert(value == bytes(reversed(v)))
+
+    v = bytes([3, 4, 5])
+    a.write_value(None, v)
+    assert(a.value == bytes(reversed(v)))
+
+    # Packed adapter with single element format
+    v = 1234
+    pv = struct.pack('>H', v)
+    c.value = v
+    a = PackedCharacteristicAdapter(c, '>H')
+
+    value = a.read_value(None)
+    assert(value == pv)
+    c.value = None
+    a.write_value(None, pv)
+    assert(a.value == v)
+
+    # Packed adapter with multi-element format
+    v1 = 1234
+    v2 = 5678
+    pv = struct.pack('>HH', v1, v2)
+    c.value = (v1, v2)
+    a = PackedCharacteristicAdapter(c, '>HH')
+
+    value = a.read_value(None)
+    assert(value == pv)
+    c.value = None
+    a.write_value(None, pv)
+    assert(a.value == (v1, v2))
+
+    # Mapped adapter
+    v1 = 1234
+    v2 = 5678
+    pv = struct.pack('>HH', v1, v2)
+    mapped = {'v1': v1, 'v2': v2}
+    c.value = mapped
+    a = MappedCharacteristicAdapter(c, '>HH', ('v1', 'v2'))
+
+    value = a.read_value(None)
+    assert(value == pv)
+    c.value = None
+    a.write_value(None, pv)
+    assert(a.value == mapped)
+
+    # UTF-8 adapter
+    v = 'Hello π'
+    ev = v.encode('utf-8')
+    c.value = v
+    a = UTF8CharacteristicAdapter(c)
+
+    value = a.read_value(None)
+    assert(value == ev)
+    c.value = None
+    a.write_value(None, ev)
+    assert(a.value == v)
+
+
+# -----------------------------------------------------------------------------
+def test_CharacteristicValue():
+    b = bytes([1, 2, 3])
+    c = CharacteristicValue(read=lambda _: b)
+    x = c.read(None)
+    assert(x == b)
+
+    result = []
+    c = CharacteristicValue(write=lambda connection, value: result.append((connection, value)))
+    z = object()
+    c.write(z, b)
+    assert(result == [(z, b)])
+
+
+# -----------------------------------------------------------------------------
+class TwoDevices:
+    def __init__(self):
+        self.connections = [None, None]
+
+        self.link = LocalLink()
+        self.controllers = [
+            Controller('C1', link = self.link),
+            Controller('C2', link = self.link)
+        ]
+        self.devices = [
+            Device(
+                address = 'F0:F1:F2:F3:F4:F5',
+                host    = Host(self.controllers[0], AsyncPipeSink(self.controllers[0]))
+            ),
+            Device(
+                address = 'F5:F4:F3:F2:F1:F0',
+                host    = Host(self.controllers[1], AsyncPipeSink(self.controllers[1]))
+            )
+        ]
+
+        self.paired = [None, None]
+
+
+# -----------------------------------------------------------------------------
+async def async_barrier():
+    ready = asyncio.get_running_loop().create_future()
+    asyncio.get_running_loop().call_soon(ready.set_result, None)
+    await ready
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_read_write():
+    [client, server] = TwoDevices().devices
+
+    characteristic1 = Characteristic(
+        'FDB159DB-036C-49E3-B3DB-6325AC750806',
+        Characteristic.READ | Characteristic.WRITE,
+        Characteristic.READABLE | Characteristic.WRITEABLE
+    )
+
+    def on_characteristic1_write(connection, value):
+        characteristic1._last_value = (connection, value)
+
+    characteristic1.on('write', on_characteristic1_write)
+
+    def on_characteristic2_read(connection):
+        return bytes(str(connection.peer_address))
+
+    def on_characteristic2_write(connection, value):
+        characteristic2._last_value = (connection, value)
+
+    characteristic2 = Characteristic(
+        '66DE9057-C848-4ACA-B993-D675644EBB85',
+        Characteristic.READ | Characteristic.WRITE,
+        Characteristic.READABLE | Characteristic.WRITEABLE,
+        CharacteristicValue(read=on_characteristic2_read, write=on_characteristic2_write)
+    )
+
+    service1 = Service(
+        '3A657F47-D34F-46B3-B1EC-698E29B6B829',
+        [
+            characteristic1,
+            characteristic2
+        ]
+    )
+    server.add_services([service1])
+
+    await client.power_on()
+    await server.power_on()
+    connection = await client.connect(server.random_address)
+    peer = Peer(connection)
+
+    await peer.discover_services()
+    await peer.discover_characteristics()
+    c = peer.get_characteristics_by_uuid(characteristic1.uuid)
+    assert(len(c) == 1)
+    c1 = c[0]
+    c = peer.get_characteristics_by_uuid(characteristic2.uuid)
+    assert(len(c) == 1)
+    c2 = c[0]
+
+    v1 = await peer.read_value(c1)
+    assert(v1 == b'')
+    b = bytes([1, 2, 3])
+    await peer.write_value(c1, b)
+    await async_barrier()
+    assert(characteristic1.value == b)
+    v1 = await peer.read_value(c1)
+    assert(v1 == b)
+    assert(type(characteristic1._last_value) is tuple)
+    assert(len(characteristic1._last_value) == 2)
+    assert(str(characteristic1._last_value[0].peer_address) == str(client.random_address))
+    assert(characteristic1._last_value[1] == b)
+    bb = bytes([3, 4, 5, 6])
+    characteristic1.value = bb
+    v1 = await peer.read_value(c1)
+    assert(v1 == bb)
+
+    await peer.write_value(c2, b)
+    await async_barrier()
+    assert(type(characteristic2._last_value) is tuple)
+    assert(len(characteristic2._last_value) == 2)
+    assert(str(characteristic2._last_value[0].peer_address) == str(client.random_address))
+    assert(characteristic2._last_value[1] == b)
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_read_write2():
+    [client, server] = TwoDevices().devices
+
+    v = bytes([0x11, 0x22, 0x33, 0x44])
+    characteristic1 = Characteristic(
+        'FDB159DB-036C-49E3-B3DB-6325AC750806',
+        Characteristic.READ | Characteristic.WRITE,
+        Characteristic.READABLE | Characteristic.WRITEABLE,
+        value=v
+    )
+
+    service1 = Service(
+        '3A657F47-D34F-46B3-B1EC-698E29B6B829',
+        [
+            characteristic1
+        ]
+    )
+    server.add_services([service1])
+
+    await client.power_on()
+    await server.power_on()
+    connection = await client.connect(server.random_address)
+    peer = Peer(connection)
+
+    await peer.discover_services()
+    c = peer.get_services_by_uuid(service1.uuid)
+    assert(len(c) == 1)
+    s = c[0]
+    await s.discover_characteristics()
+    c = s.get_characteristics_by_uuid(characteristic1.uuid)
+    assert(len(c) == 1)
+    c1 = c[0]
+
+    v1 = await c1.read_value()
+    assert(v1 == v)
+
+    a1 = PackedCharacteristicAdapter(c1, '>I')
+    v1 = await a1.read_value()
+    assert(v1 == struct.unpack('>I', v)[0])
+
+    b = bytes([0x55, 0x66, 0x77, 0x88])
+    await a1.write_value(struct.unpack('>I', b)[0])
+    await async_barrier()
+    assert(characteristic1.value == b)
+    v1 = await a1.read_value()
+    assert(v1 == struct.unpack('>I', b)[0])
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_subscribe_notify():
+    [client, server] = TwoDevices().devices
+
+    characteristic1 = Characteristic(
+        'FDB159DB-036C-49E3-B3DB-6325AC750806',
+        Characteristic.READ | Characteristic.NOTIFY,
+        Characteristic.READABLE,
+        bytes([1, 2, 3])
+    )
+
+    def on_characteristic1_subscription(connection, notify_enabled, indicate_enabled):
+        characteristic1._last_subscription = (connection, notify_enabled, indicate_enabled)
+
+    characteristic1.on('subscription', on_characteristic1_subscription)
+
+    characteristic2 = Characteristic(
+        '66DE9057-C848-4ACA-B993-D675644EBB85',
+        Characteristic.READ | Characteristic.INDICATE,
+        Characteristic.READABLE,
+        bytes([4, 5, 6])
+    )
+
+    def on_characteristic2_subscription(connection, notify_enabled, indicate_enabled):
+        characteristic2._last_subscription = (connection, notify_enabled, indicate_enabled)
+
+    characteristic2.on('subscription', on_characteristic2_subscription)
+
+    characteristic3 = Characteristic(
+        'AB5E639C-40C1-4238-B9CB-AF41F8B806E4',
+        Characteristic.READ | Characteristic.NOTIFY | Characteristic.INDICATE,
+        Characteristic.READABLE,
+        bytes([7, 8, 9])
+    )
+
+    def on_characteristic3_subscription(connection, notify_enabled, indicate_enabled):
+        characteristic3._last_subscription = (connection, notify_enabled, indicate_enabled)
+
+    characteristic3.on('subscription', on_characteristic3_subscription)
+
+    service1 = Service(
+        '3A657F47-D34F-46B3-B1EC-698E29B6B829',
+        [
+            characteristic1,
+            characteristic2,
+            characteristic3
+        ]
+    )
+    server.add_services([service1])
+
+    def on_characteristic_subscription(connection, characteristic, notify_enabled, indicate_enabled):
+        server._last_subscription = (connection, characteristic, notify_enabled, indicate_enabled)
+
+    server.on('characteristic_subscription', on_characteristic_subscription)
+
+    await client.power_on()
+    await server.power_on()
+    connection = await client.connect(server.random_address)
+    peer = Peer(connection)
+
+    await peer.discover_services()
+    await peer.discover_characteristics()
+    c = peer.get_characteristics_by_uuid(characteristic1.uuid)
+    assert(len(c) == 1)
+    c1 = c[0]
+    c = peer.get_characteristics_by_uuid(characteristic2.uuid)
+    assert(len(c) == 1)
+    c2 = c[0]
+    c = peer.get_characteristics_by_uuid(characteristic3.uuid)
+    assert(len(c) == 1)
+    c3 = c[0]
+
+    c1._last_update = None
+
+    def on_c1_update(connection, value):
+        c1._last_update = (connection, value)
+
+    c1.on('update', on_c1_update)
+    await peer.subscribe(c1)
+    await async_barrier()
+    assert(server._last_subscription[1] == characteristic1)
+    assert(server._last_subscription[2])
+    assert(not server._last_subscription[3])
+    assert(characteristic1._last_subscription[1])
+    assert(not characteristic1._last_subscription[2])
+    await server.indicate_subscribers(characteristic1)
+    await async_barrier()
+    assert(c1._last_update is None)
+    await server.notify_subscribers(characteristic1)
+    await async_barrier()
+    assert(c1._last_update is not None)
+    assert(c1._last_update[1] == characteristic1.value)
+
+    c2._last_update = None
+
+    def on_c2_update(value):
+        c2._last_update = (connection, value)
+
+    await peer.subscribe(c2, on_c2_update)
+    await async_barrier()
+    await server.notify_subscriber(characteristic2._last_subscription[0], characteristic2)
+    await async_barrier()
+    assert(c2._last_update is None)
+    await server.indicate_subscriber(characteristic2._last_subscription[0], characteristic2)
+    await async_barrier()
+    assert(c2._last_update is not None)
+    assert(c2._last_update[1] == characteristic2.value)
+
+    c3._last_update = None
+
+    def on_c3_update(connection, value):
+        c3._last_update = (connection, value)
+
+    c3.on('update', on_c3_update)
+    await peer.subscribe(c3)
+    await async_barrier()
+    await server.notify_subscriber(characteristic3._last_subscription[0], characteristic3)
+    await async_barrier()
+    assert(c3._last_update is not None)
+    assert(c3._last_update[1] == characteristic3.value)
+    characteristic3.value = bytes([1, 2, 3])
+    await server.indicate_subscriber(characteristic3._last_subscription[0], characteristic3)
+    await async_barrier()
+    assert(c3._last_update is not None)
+    assert(c3._last_update[1] == characteristic3.value)
+
+
+# -----------------------------------------------------------------------------
+async def async_main():
+    await test_read_write()
+    await test_read_write2()
+    await test_subscribe_notify()
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    test_UUID()
+    test_ATT_Error_Response()
+    test_ATT_Read_By_Group_Type_Request()
+    test_CharacteristicValue()
+    test_CharacteristicAdapter()
+    asyncio.run(async_main())
diff --git a/tests/hci_data_001.bin b/tests/hci_data_001.bin
new file mode 100644
index 0000000..27f36d8
--- /dev/null
+++ b/tests/hci_data_001.bin
Binary files differ
diff --git a/tests/hci_test.py b/tests/hci_test.py
new file mode 100644
index 0000000..c4da67e
--- /dev/null
+++ b/tests/hci_test.py
@@ -0,0 +1,406 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.hci import *
+
+
+# -----------------------------------------------------------------------------
+def basic_check(x):
+    packet = x.to_bytes()
+    print(packet.hex())
+    parsed = HCI_Packet.from_bytes(packet)
+    x_str = str(x)
+    parsed_str = str(parsed)
+    print(x_str)
+    parsed_bytes = parsed.to_bytes()
+    assert(x_str == parsed_str)
+    assert(packet == parsed_bytes)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Event():
+    event = HCI_Event(0xF9)
+    basic_check(event)
+
+    event = HCI_Event(0xF8, bytes.fromhex('AABBCC'))
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Connection_Complete_Event():
+    address = Address('00:11:22:33:44:55')
+    event = HCI_LE_Connection_Complete_Event(
+        status = HCI_SUCCESS,
+        connection_handle = 1,
+        role = 1,
+        peer_address_type = 1,
+        peer_address = address,
+        conn_interval = 3,
+        conn_latency = 4,
+        supervision_timeout = 5,
+        master_clock_accuracy = 6
+    )
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Advertising_Report_Event():
+    address = Address('00:11:22:33:44:55')
+    report = HCI_Object(
+        HCI_LE_Advertising_Report_Event.REPORT_FIELDS,
+        event_type   = HCI_LE_Advertising_Report_Event.ADV_IND,
+        address_type = Address.PUBLIC_DEVICE_ADDRESS,
+        address      = address,
+        data         = bytes.fromhex('0201061106ba5689a6fabfa2bd01467d6e00fbabad08160a181604659b03'),
+        rssi         = 100
+    )
+    event = HCI_LE_Advertising_Report_Event([report])
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Read_Remote_Features_Complete_Event():
+    event = HCI_LE_Read_Remote_Features_Complete_Event(
+        status            = HCI_SUCCESS,
+        connection_handle = 0x007,
+        le_features       = bytes.fromhex('0011223344556677')
+    )
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Connection_Update_Complete_Event():
+    event = HCI_LE_Connection_Update_Complete_Event(
+        status              = HCI_SUCCESS,
+        connection_handle   = 0x007,
+        conn_interval       = 10,
+        conn_latency        = 3,
+        supervision_timeout = 5
+    )
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Channel_Selection_Algorithm_Event():
+    event = HCI_LE_Channel_Selection_Algorithm_Event(
+        connection_handle = 7,
+        channel_selection_algorithm = 1
+    )
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Command_Complete_Event():
+    # With a serializable object
+    event = HCI_Command_Complete_Event(
+        num_hci_command_packets = 34,
+        command_opcode = HCI_LE_READ_BUFFER_SIZE_COMMAND,
+        return_parameters = HCI_LE_Read_Buffer_Size_Command.create_return_parameters(
+            status                           = 0,
+            hc_le_acl_data_packet_length     = 1234,
+            hc_total_num_le_acl_data_packets = 56
+        )
+    )
+    basic_check(event)
+
+    # With an arbitrary byte array
+    event = HCI_Command_Complete_Event(
+        num_hci_command_packets = 1,
+        command_opcode = HCI_RESET_COMMAND,
+        return_parameters = bytes([1, 2, 3, 4])
+    )
+    basic_check(event)
+
+    # With a simple status as a 1-byte array
+    event = HCI_Command_Complete_Event(
+        num_hci_command_packets = 1,
+        command_opcode = HCI_RESET_COMMAND,
+        return_parameters = bytes([7])
+    )
+    basic_check(event)
+    event = HCI_Packet.from_bytes(event.to_bytes())
+    assert(event.return_parameters == 7)
+
+    # With a simple status as an integer status
+    event = HCI_Command_Complete_Event(
+        num_hci_command_packets = 1,
+        command_opcode = HCI_RESET_COMMAND,
+        return_parameters = 9
+    )
+    basic_check(event)
+    assert(event.return_parameters == 9)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Command_Status_Event():
+    event = HCI_Command_Status_Event(
+        status = 0,
+        num_hci_command_packets = 37,
+        command_opcode = HCI_DISCONNECT_COMMAND
+    )
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Number_Of_Completed_Packets_Event():
+    event = HCI_Number_Of_Completed_Packets_Event([
+        (1, 2),
+        (3, 4)
+    ])
+    basic_check(event)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Command():
+    command = HCI_Command(0x5566)
+    basic_check(command)
+
+    command = HCI_Command(0x5566, bytes.fromhex('AABBCC'))
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Reset_Command():
+    command = HCI_Reset_Command()
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Read_Local_Version_Information_Command():
+    command = HCI_Read_Local_Version_Information_Command()
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Read_Local_Supported_Commands_Command():
+    command = HCI_Read_Local_Supported_Commands_Command()
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Read_Local_Supported_Features_Command():
+    command = HCI_Read_Local_Supported_Features_Command()
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Disconnect_Command():
+    command = HCI_Disconnect_Command(
+        connection_handle = 123,
+        reason = 0x11
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Set_Event_Mask_Command():
+    command = HCI_Set_Event_Mask_Command(
+        event_mask = bytes.fromhex('0011223344556677')
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Event_Mask_Command():
+    command = HCI_LE_Set_Event_Mask_Command(
+        le_event_mask = bytes.fromhex('0011223344556677')
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Random_Address_Command():
+    command = HCI_LE_Set_Random_Address_Command(
+        random_address = Address('00:11:22:33:44:55')
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Advertising_Parameters_Command():
+    command = HCI_LE_Set_Advertising_Parameters_Command(
+        advertising_interval_min   = 20,
+        advertising_interval_max   = 30,
+        advertising_type           = HCI_LE_Set_Advertising_Parameters_Command.ADV_NONCONN_IND,
+        own_address_type           = Address.PUBLIC_DEVICE_ADDRESS,
+        peer_address_type          = Address.RANDOM_DEVICE_ADDRESS,
+        peer_address               = Address('00:11:22:33:44:55'),
+        advertising_channel_map    = 0x03,
+        advertising_filter_policy  = 1
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Advertising_Data_Command():
+    command = HCI_LE_Set_Advertising_Data_Command(
+        advertising_data = bytes.fromhex('AABBCC')
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Scan_Parameters_Command():
+    command = HCI_LE_Set_Scan_Parameters_Command(
+        le_scan_type           = 1,
+        le_scan_interval       = 20,
+        le_scan_window         = 10,
+        own_address_type       = 1,
+        scanning_filter_policy = 0
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Scan_Enable_Command():
+    command = HCI_LE_Set_Scan_Enable_Command(
+        le_scan_enable    = 1,
+        filter_duplicates = 0
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Create_Connection_Command():
+    command = HCI_LE_Create_Connection_Command(
+        le_scan_interval        = 4,
+        le_scan_window          = 5,
+        initiator_filter_policy = 1,
+        peer_address_type       = 1,
+        peer_address            = Address('00:11:22:33:44:55'),
+        own_address_type        = 2,
+        conn_interval_min       = 7,
+        conn_interval_max       = 8,
+        conn_latency            = 9,
+        supervision_timeout     = 10,
+        minimum_ce_length       = 11,
+        maximum_ce_length       = 12
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Add_Device_To_White_List_Command():
+    command = HCI_LE_Add_Device_To_White_List_Command(
+        address_type = 1,
+        address      = Address('00:11:22:33:44:55')
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Remove_Device_From_White_List_Command():
+    command = HCI_LE_Remove_Device_From_White_List_Command(
+        address_type = 1,
+        address      = Address('00:11:22:33:44:55')
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Connection_Update_Command():
+    command = HCI_LE_Connection_Update_Command(
+        connection_handle   = 0x0002,
+        conn_interval_min   = 10,
+        conn_interval_max   = 20,
+        conn_latency        = 7,
+        supervision_timeout = 3,
+        minimum_ce_length   = 100,
+        maximum_ce_length   = 200
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Read_Remote_Features_Command():
+    command = HCI_LE_Read_Remote_Features_Command(
+        connection_handle   = 0x0002
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_LE_Set_Default_PHY_Command():
+    command = HCI_LE_Set_Default_PHY_Command(
+        all_phys = 0,
+        tx_phys  = 1,
+        rx_phys  = 1
+    )
+    basic_check(command)
+
+
+# -----------------------------------------------------------------------------
+def test_address():
+    a = Address('C4:F2:17:1A:1D:BB')
+    assert(not a.is_public)
+    assert(a.is_random)
+    assert(a.address_type == Address.RANDOM_DEVICE_ADDRESS)
+    assert(not a.is_resolvable)
+    assert(not a.is_resolved)
+    assert(a.is_static)
+
+
+# -----------------------------------------------------------------------------
+def test_custom():
+    data = bytes([0x77, 0x02, 0x01, 0x03])
+    packet = HCI_CustomPacket(data)
+    assert(packet.hci_packet_type == 0x77)
+    assert(packet.payload == data)
+
+
+# -----------------------------------------------------------------------------
+def run_test_events():
+    test_HCI_Event()
+    test_HCI_LE_Connection_Complete_Event()
+    test_HCI_LE_Advertising_Report_Event()
+    test_HCI_LE_Connection_Update_Complete_Event()
+    test_HCI_LE_Read_Remote_Features_Complete_Event()
+    test_HCI_LE_Channel_Selection_Algorithm_Event()
+    test_HCI_Command_Complete_Event()
+    test_HCI_Command_Status_Event()
+    test_HCI_Number_Of_Completed_Packets_Event()
+
+
+# -----------------------------------------------------------------------------
+def run_test_commands():
+    test_HCI_Command()
+    test_HCI_Reset_Command()
+    test_HCI_Read_Local_Version_Information_Command()
+    test_HCI_Read_Local_Supported_Commands_Command()
+    test_HCI_Read_Local_Supported_Features_Command()
+    test_HCI_Disconnect_Command()
+    test_HCI_Set_Event_Mask_Command()
+    test_HCI_LE_Set_Event_Mask_Command()
+    test_HCI_LE_Set_Random_Address_Command()
+    test_HCI_LE_Set_Advertising_Parameters_Command()
+    test_HCI_LE_Set_Advertising_Data_Command()
+    test_HCI_LE_Set_Scan_Parameters_Command()
+    test_HCI_LE_Set_Scan_Enable_Command()
+    test_HCI_LE_Create_Connection_Command()
+    test_HCI_LE_Add_Device_To_White_List_Command()
+    test_HCI_LE_Remove_Device_From_White_List_Command()
+    test_HCI_LE_Connection_Update_Command()
+    test_HCI_LE_Read_Remote_Features_Command()
+    test_HCI_LE_Set_Default_PHY_Command()
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    run_test_events()
+    run_test_commands()
+    test_address()
+    test_custom()
diff --git a/tests/import_test.py b/tests/import_test.py
new file mode 100644
index 0000000..d6eafbd
--- /dev/null
+++ b/tests/import_test.py
@@ -0,0 +1,67 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+
+
+# -----------------------------------------------------------------------------
+def test_import():
+    from bumble import (
+        att,
+        bridge,
+        company_ids,
+        controller,
+        core,
+        crypto,
+        device,
+        gap,
+        hci,
+        hfp,
+        host,
+        keys,
+        l2cap,
+        link,
+        rfcomm,
+        sdp,
+        smp,
+        transport,
+        utils
+    )
+
+    assert att
+    assert bridge
+    assert company_ids
+    assert controller
+    assert core
+    assert crypto
+    assert device
+    assert gap
+    assert hci
+    assert hfp
+    assert host
+    assert keys
+    assert l2cap
+    assert link
+    assert rfcomm
+    assert sdp
+    assert smp
+    assert transport
+    assert utils
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    test_import()
diff --git a/tests/pytest.ini b/tests/pytest.ini
new file mode 100644
index 0000000..6796337
--- /dev/null
+++ b/tests/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+junit_logging = all
+asyncio_mode = auto
\ No newline at end of file
diff --git a/tests/rfcomm_test.py b/tests/rfcomm_test.py
new file mode 100644
index 0000000..6f1c44f
--- /dev/null
+++ b/tests/rfcomm_test.py
@@ -0,0 +1,48 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.rfcomm import RFCOMM_Frame
+
+
+# -----------------------------------------------------------------------------
+def basic_frame_check(x):
+    serialized = bytes(x)
+    if len(serialized) < 500:
+        print('Original:', x)
+        print('Serialized:', serialized.hex())
+    parsed = RFCOMM_Frame.from_bytes(serialized)
+    if len(serialized) < 500:
+        print('Parsed:', parsed)
+    parsed_bytes = bytes(parsed)
+    if len(serialized) < 500:
+        print('Parsed Bytes:', parsed_bytes.hex())
+    assert(parsed_bytes == serialized)
+    x_str = str(x)
+    parsed_str = str(parsed)
+    assert(x_str == parsed_str)
+
+
+# -----------------------------------------------------------------------------
+def test_frames():
+    data = bytes.fromhex('033f011c')
+    frame = RFCOMM_Frame.from_bytes(data)
+    basic_frame_check(frame)
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    test_frames()
diff --git a/tests/sdp_test.py b/tests/sdp_test.py
new file mode 100644
index 0000000..d7cf8be
--- /dev/null
+++ b/tests/sdp_test.py
@@ -0,0 +1,148 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.sdp import *
+from bumble.core import UUID
+
+
+# -----------------------------------------------------------------------------
+def basic_check(x):
+    serialized = bytes(x)
+    if len(serialized) < 500:
+        print('Original:', x)
+        print('Serialized:', serialized.hex())
+    parsed = DataElement.from_bytes(serialized)
+    if len(serialized) < 500:
+        print('Parsed:', parsed)
+    parsed_bytes = bytes(parsed)
+    if len(serialized) < 500:
+        print('Parsed Bytes:', parsed_bytes.hex())
+    assert(parsed_bytes == serialized)
+    x_str = str(x)
+    parsed_str = str(parsed)
+    assert(x_str == parsed_str)
+
+
+# -----------------------------------------------------------------------------
+def test_data_elements():
+    e = DataElement(DataElement.NIL, None)
+    basic_check(e)
+
+    e = DataElement(DataElement.UNSIGNED_INTEGER, 12, 1)
+    basic_check(e)
+
+    e = DataElement(DataElement.UNSIGNED_INTEGER, 1234, 2)
+    basic_check(e)
+
+    e = DataElement(DataElement.UNSIGNED_INTEGER, 0x123456, 4)
+    basic_check(e)
+
+    e = DataElement(DataElement.UNSIGNED_INTEGER, 0x123456789, 8)
+    basic_check(e)
+
+    e = DataElement(DataElement.UNSIGNED_INTEGER, 0x0000FFFF, value_size=4)
+    basic_check(e)
+
+    e = DataElement(DataElement.SIGNED_INTEGER, -12, 1)
+    basic_check(e)
+
+    e = DataElement(DataElement.SIGNED_INTEGER, -1234, 2)
+    basic_check(e)
+
+    e = DataElement(DataElement.SIGNED_INTEGER, -0x123456, 4)
+    basic_check(e)
+
+    e = DataElement(DataElement.SIGNED_INTEGER, -0x123456789, 8)
+    basic_check(e)
+
+    e = DataElement(DataElement.SIGNED_INTEGER, 0x0000FFFF, value_size=4)
+    basic_check(e)
+
+    e = DataElement(DataElement.UUID, UUID.from_16_bits(1234))
+    basic_check(e)
+
+    e = DataElement(DataElement.UUID, UUID.from_32_bits(123456789))
+    basic_check(e)
+
+    e = DataElement(DataElement.UUID, UUID('61A3512C-09BE-4DDC-A6A6-0B03667AAFC6'))
+    basic_check(e)
+
+    e = DataElement(DataElement.TEXT_STRING, 'hello')
+    basic_check(e)
+
+    e = DataElement(DataElement.TEXT_STRING, 'hello' * 60)
+    basic_check(e)
+
+    e = DataElement(DataElement.TEXT_STRING, 'hello' * 20000)
+    basic_check(e)
+
+    e = DataElement(DataElement.BOOLEAN, True)
+    basic_check(e)
+
+    e = DataElement(DataElement.BOOLEAN, False)
+    basic_check(e)
+
+    e = DataElement(DataElement.SEQUENCE, [DataElement(DataElement.BOOLEAN, True)])
+    basic_check(e)
+
+    e = DataElement(DataElement.SEQUENCE, [
+        DataElement(DataElement.BOOLEAN, True),
+        DataElement(DataElement.TEXT_STRING, 'hello')
+    ])
+    basic_check(e)
+
+    e = DataElement(DataElement.ALTERNATIVE, [DataElement(DataElement.BOOLEAN, True)])
+    basic_check(e)
+
+    e = DataElement(DataElement.ALTERNATIVE, [
+        DataElement(DataElement.BOOLEAN, True),
+        DataElement(DataElement.TEXT_STRING, 'hello')
+    ])
+    basic_check(e)
+
+    e = DataElement(DataElement.URL, 'http://example.com')
+
+    e = DataElement.nil()
+
+    e = DataElement.unsigned_integer(1234, 2)
+    basic_check(e)
+
+    e = DataElement.signed_integer(-1234, 2)
+    basic_check(e)
+
+    e = DataElement.uuid(UUID.from_16_bits(1234))
+    basic_check(e)
+
+    e = DataElement.text_string('hello')
+    basic_check(e)
+
+    e = DataElement.boolean(True)
+    basic_check(e)
+
+    e = DataElement.sequence([DataElement.signed_integer(0, 1), DataElement.text_string('hello')])
+    basic_check(e)
+
+    e = DataElement.alternative([DataElement.signed_integer(0, 1), DataElement.text_string('hello')])
+    basic_check(e)
+
+    e = DataElement.url('http://foobar.com')
+    basic_check(e)
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    test_data_elements()
diff --git a/tests/self_test.py b/tests/self_test.py
new file mode 100644
index 0000000..a1d2ddd
--- /dev/null
+++ b/tests/self_test.py
@@ -0,0 +1,333 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import itertools
+import logging
+import os
+import pytest
+
+from bumble.controller import Controller
+from bumble.link import LocalLink
+from bumble.device import Device, Peer
+from bumble.host import Host
+from bumble.gatt import Service, Characteristic
+from bumble.transport import AsyncPipeSink
+from bumble.smp import (
+    PairingConfig,
+    PairingDelegate,
+    SMP_PAIRING_NOT_SUPPORTED_ERROR,
+    SMP_CONFIRM_VALUE_FAILED_ERROR,
+    SMP_ID_KEY_DISTRIBUTION_FLAG,
+)
+from bumble.core import ProtocolError
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+class TwoDevices:
+    def __init__(self):
+        self.connections = [None, None]
+
+        self.link = LocalLink()
+        self.controllers = [
+            Controller('C1', link = self.link),
+            Controller('C2', link = self.link)
+        ]
+        self.devices = [
+            Device(
+                address = 'F0:F1:F2:F3:F4:F5',
+                host    = Host(self.controllers[0], AsyncPipeSink(self.controllers[0]))
+            ),
+            Device(
+                address = 'F5:F4:F3:F2:F1:F0',
+                host    = Host(self.controllers[1], AsyncPipeSink(self.controllers[1]))
+            )
+        ]
+
+        self.paired = [None, None]
+
+    def on_connection(self, which, connection):
+        self.connections[which] = connection
+
+    def on_paired(self, which, keys):
+        self.paired[which] = keys
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_self_connection():
+    # Create two devices, each with a controller, attached to the same link
+    two_devices = TwoDevices()
+
+    # Attach listeners
+    two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection))
+    two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection))
+
+    # Start
+    await two_devices.devices[0].power_on()
+    await two_devices.devices[1].power_on()
+
+    # Connect the two devices
+    await two_devices.devices[0].connect(two_devices.devices[1].random_address)
+
+    # Check the post conditions
+    assert(two_devices.connections[0] is not None)
+    assert(two_devices.connections[1] is not None)
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_self_gatt():
+    # Create two devices, each with a controller, attached to the same link
+    two_devices = TwoDevices()
+
+    # Add some GATT characteristics to device 1
+    c1 = Characteristic(
+        '3A143AD7-D4A7-436B-97D6-5B62C315E833',
+        Characteristic.READ,
+        Characteristic.READABLE,
+        bytes([1, 2, 3])
+    )
+    c2 = Characteristic(
+        '9557CCE2-DB37-46EB-94C4-50AE5B9CB0F8',
+        Characteristic.READ | Characteristic.WRITE,
+        Characteristic.READABLE | Characteristic.WRITEABLE,
+        bytes([4, 5, 6])
+    )
+    c3 = Characteristic(
+        '84FC1A2E-C52D-4A2D-B8C3-8855BAB86638',
+        Characteristic.READ | Characteristic.WRITE_WITHOUT_RESPONSE,
+        Characteristic.READABLE | Characteristic.WRITEABLE,
+        bytes([7, 8, 9])
+    )
+    c4 = Characteristic(
+        '84FC1A2E-C52D-4A2D-B8C3-8855BAB86638',
+        Characteristic.READ | Characteristic.NOTIFY | Characteristic.INDICATE,
+        Characteristic.READABLE,
+        bytes([1, 1, 1])
+    )
+
+    s1 = Service('8140E247-04F0-42C1-BC34-534C344DAFCA', [c1, c2, c3])
+    s2 = Service('97210A0F-1875-4D05-9E5D-326EB171257A', [c4])
+    two_devices.devices[1].add_services([s1, s2])
+
+    # Start
+    await two_devices.devices[0].power_on()
+    await two_devices.devices[1].power_on()
+
+    # Connect the two devices
+    connection = await two_devices.devices[0].connect(two_devices.devices[1].random_address)
+    peer = Peer(connection)
+
+    bogus_uuid = 'A0AA6007-0B48-4BBE-80AC-0DE9AAF541EA'
+    result = await peer.discover_services([bogus_uuid])
+    assert(result == [])
+    services = peer.get_services_by_uuid(bogus_uuid)
+    assert(len(services) == 0)
+
+    result = await peer.discover_service(s1.uuid)
+    assert(len(result) == 1)
+    services = peer.get_services_by_uuid(s1.uuid)
+    assert(len(services) == 1)
+    s = services[0]
+    assert(services[0].uuid == s1.uuid)
+
+    result = await peer.discover_characteristics([c1.uuid], s)
+    assert(len(result) == 1)
+    characteristics = peer.get_characteristics_by_uuid(c1.uuid)
+    assert(len(characteristics) == 1)
+    c = characteristics[0]
+    assert(c.uuid == c1.uuid)
+    result = await peer.read_value(c)
+    assert(result is not None)
+    assert(result == c1.value)
+
+
+# -----------------------------------------------------------------------------
+async def _test_self_smp_with_configs(pairing_config1, pairing_config2):
+    # Create two devices, each with a controller, attached to the same link
+    two_devices = TwoDevices()
+
+    # Start
+    await two_devices.devices[0].power_on()
+    await two_devices.devices[1].power_on()
+
+    # Attach listeners
+    two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection))
+    two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection))
+
+    # Connect the two devices
+    connection = await two_devices.devices[0].connect(two_devices.devices[1].random_address)
+    assert(not connection.is_encrypted)
+
+    # Attach connection listeners
+    two_devices.connections[0].on('pairing', lambda keys: two_devices.on_paired(0, keys))
+    two_devices.connections[1].on('pairing', lambda keys: two_devices.on_paired(1, keys))
+
+    # Set up the pairing configs
+    if pairing_config1:
+        two_devices.devices[0].pairing_config_factory = lambda connection: pairing_config1
+    if pairing_config2:
+        two_devices.devices[1].pairing_config_factory = lambda connection: pairing_config2
+
+    # Pair
+    await two_devices.devices[0].pair(connection)
+    assert(connection.is_encrypted)
+    assert(two_devices.paired[0] is not None)
+    assert(two_devices.paired[1] is not None)
+
+
+# -----------------------------------------------------------------------------
+IO_CAP = [
+    PairingDelegate.NO_OUTPUT_NO_INPUT,
+    PairingDelegate.KEYBOARD_INPUT_ONLY,
+    PairingDelegate.DISPLAY_OUTPUT_ONLY,
+    PairingDelegate.DISPLAY_OUTPUT_AND_YES_NO_INPUT,
+    PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT
+]
+SC = [False, True]
+MITM = [False, True]
+# Key distribution is a 4-bit bitmask
+# IdKey is necessary for current SMP structure
+KEY_DIST = [i for i in range(16) if (i & SMP_ID_KEY_DISTRIBUTION_FLAG)]
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize('io_cap, sc, mitm, key_dist',
+    itertools.product(IO_CAP, SC, MITM, KEY_DIST)
+)
+async def test_self_smp(io_cap, sc, mitm, key_dist):
+    class Delegate(PairingDelegate):
+        def __init__(self, name, io_capability, local_initiator_key_distribution, local_responder_key_distribution):
+            super().__init__(io_capability, local_initiator_key_distribution,
+                             local_responder_key_distribution)
+            self.name = name
+            self.reset()
+
+        def reset(self):
+            self.peer_delegate = None
+            self.number = asyncio.get_running_loop().create_future()
+
+        async def compare_numbers(self, number, digits):
+            if self.peer_delegate is None:
+                logger.warn(f'[{self.name}] no peer delegate')
+                return False
+            await self.display_number(number, digits=6)
+            logger.debug(f'[{self.name}] waiting for peer number')
+            peer_number = await self.peer_delegate.number
+            logger.debug(f'[{self.name}] comparing numbers: {number} and {peer_number}')
+            return number == peer_number
+
+        async def get_number(self):
+            if self.peer_delegate is None:
+                logger.warn(f'[{self.name}] no peer delegate')
+                return 0
+            else:
+                if self.peer_delegate.io_capability == PairingDelegate.KEYBOARD_INPUT_ONLY:
+                    peer_number = 6789
+                else:
+                    logger.debug(f'[{self.name}] waiting for peer number')
+                    peer_number = await self.peer_delegate.number
+                logger.debug(f'[{self.name}] returning number: {peer_number}')
+                return peer_number
+
+        async def display_number(self, number, digits):
+            logger.debug(f'[{self.name}] displaying number: {number}')
+            self.number.set_result(number)
+
+        def __str__(self):
+            return f'Delegate(name={self.name}, io_capability={self.io_capability})'
+
+    pairing_config_sets = [('Initiator', [None]), ('Responder', [None])]
+    for pairing_config_set in pairing_config_sets:
+        delegate = Delegate(pairing_config_set[0], io_cap, key_dist, key_dist)
+        pairing_config_set[1].append(PairingConfig(sc, mitm, True, delegate))
+
+    for pairing_config1 in pairing_config_sets[0][1]:
+        for pairing_config2 in pairing_config_sets[1][1]:
+            logger.info(f'########## self_smp with {pairing_config1} and {pairing_config2}')
+            if pairing_config1:
+                pairing_config1.delegate.reset()
+            if pairing_config2:
+                pairing_config2.delegate.reset()
+            if pairing_config1 and pairing_config2:
+                pairing_config1.delegate.peer_delegate = pairing_config2.delegate
+                pairing_config2.delegate.peer_delegate = pairing_config1.delegate
+
+            await _test_self_smp_with_configs(pairing_config1, pairing_config2)
+            
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_self_smp_reject():
+    class RejectingDelegate(PairingDelegate):
+        def __init__(self):
+            super().__init__(PairingDelegate.NO_OUTPUT_NO_INPUT)
+
+        async def accept(self):
+            return False
+
+    rejecting_pairing_config = PairingConfig(delegate = RejectingDelegate())
+    paired = False
+    try:
+        await _test_self_smp_with_configs(None, rejecting_pairing_config)
+        paired = True
+    except ProtocolError as error:
+        assert(error.error_code == SMP_PAIRING_NOT_SUPPORTED_ERROR)
+
+    assert(not paired)
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_self_smp_wrong_pin():
+    class WrongPinDelegate(PairingDelegate):
+        def __init__(self):
+            super().__init__(PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT)
+
+        async def compare_numbers(self, number, digits):
+            return False
+
+    wrong_pin_pairing_config = PairingConfig(delegate = WrongPinDelegate())
+    paired = False
+    try:
+        await _test_self_smp_with_configs(wrong_pin_pairing_config, wrong_pin_pairing_config)
+        paired = True
+    except ProtocolError as error:
+        assert(error.error_code == SMP_CONFIRM_VALUE_FAILED_ERROR)
+
+    assert(not paired)
+
+
+# -----------------------------------------------------------------------------
+async def run_test_self():
+    await test_self_connection()
+    await test_self_gatt()
+    await test_self_smp()
+    await test_self_smp_reject()
+    await test_self_smp_wrong_pin()
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
+    asyncio.run(run_test_self())
diff --git a/tests/smp_test.py b/tests/smp_test.py
new file mode 100644
index 0000000..9120c47
--- /dev/null
+++ b/tests/smp_test.py
@@ -0,0 +1,214 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.crypto import *
+
+
+# -----------------------------------------------------------------------------
+def reversed_hex(hex):
+    return bytes(reversed(bytes.fromhex(hex)))
+
+
+# -----------------------------------------------------------------------------
+def test_ecc():
+    key = EccKey.generate()
+    x = key.x
+    y = key.y
+
+    assert(len(x) == 32)
+    assert(len(y) == 32)
+
+    # Test DH with test vectors from the spec
+    private_A  = '3f49f6d4 a3c55f38 74c9b3e3 d2103f50 4aff607b eb40b799 5899b8a6 cd3c1abd'
+    private_B  = '55188b3d 32f6bb9a 900afcfb eed4e72a 59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd'
+    public_A_x = '20b003d2 f297be2c 5e2c83a7 e9f9a5b9 eff49111 acf4fddb cc030148 0e359de6'
+    public_A_y = 'dc809c49 652aeb6d 63329abf 5a52155c 766345c2 8fed3024 741c8ed0 1589d28b'
+    public_B_x = '1ea1f0f0 1faf1d96 09592284 f19e4c00 47b58afd 8615a69f 559077b2 2faaa190'
+    public_B_y = '4c55f33e 429dad37 7356703a 9ab85160 472d1130 e28e3676 5f89aff9 15b1214a'
+    dhkey      = 'ec0234a3 57c8ad05 341010a6 0a397d9b 99796b13 b4f866f1 868d34f3 73bfa698'
+
+    key_a = EccKey.from_private_key_bytes(
+        bytes.fromhex(private_A),
+        bytes.fromhex(public_A_x),
+        bytes.fromhex(public_A_y)
+    )
+    shared_key = key_a.dh(
+        bytes.fromhex(public_B_x),
+        bytes.fromhex(public_B_y)
+    )
+    assert(shared_key == bytes.fromhex(dhkey))
+
+    key_b = EccKey.from_private_key_bytes(
+        bytes.fromhex(private_B),
+        bytes.fromhex(public_B_x),
+        bytes.fromhex(public_B_y)
+    )
+    shared_key = key_b.dh(
+        bytes.fromhex(public_A_x),
+        bytes.fromhex(public_A_y)
+    )
+    assert(shared_key == bytes.fromhex(dhkey))
+
+
+# -----------------------------------------------------------------------------
+def test_c1():
+    k = bytes(16)
+    r = reversed_hex('5783D52156AD6F0E6388274EC6702EE0')
+    pres = reversed_hex('05000800000302')
+    preq = reversed_hex('07071000000101')
+    iat = 1
+    ia = reversed_hex('A1A2A3A4A5A6')
+    rat = 0
+    ra = reversed_hex('B1B2B3B4B5B6')
+    result = c1(k, r, preq, pres, iat, rat, ia, ra)
+    assert(result == reversed_hex('1e1e3fef878988ead2a74dc5bef13b86'))
+
+
+# -----------------------------------------------------------------------------
+def test_s1():
+    k = bytes(16)
+    r1 = reversed_hex('000F0E0D0C0B0A091122334455667788')
+    r2 = reversed_hex('010203040506070899AABBCCDDEEFF00')
+    result = s1(k, r1, r2)
+    assert(result == reversed_hex('9a1fe1f0e8b0f49b5b4216ae796da062'))
+
+
+# -----------------------------------------------------------------------------
+def test_aes_cmac():
+    m = b''
+    k = bytes.fromhex('2b7e1516 28aed2a6 abf71588 09cf4f3c')
+    cmac = aes_cmac(m, k)
+    assert(cmac == bytes.fromhex('bb1d6929 e9593728 7fa37d12 9b756746'))
+
+    m = bytes.fromhex('6bc1bee2 2e409f96 e93d7e11 7393172a')
+    cmac = aes_cmac(m, k)
+    assert(cmac == bytes.fromhex('070a16b4 6b4d4144 f79bdd9d d04a287c'))
+
+    m = bytes.fromhex(
+        '6bc1bee2 2e409f96 e93d7e11 7393172a' +
+        'ae2d8a57 1e03ac9c 9eb76fac 45af8e51' +
+        '30c81c46 a35ce411'
+    )
+    cmac = aes_cmac(m, k)
+    assert(cmac == bytes.fromhex('dfa66747 de9ae630 30ca3261 1497c827'))
+
+    m = bytes.fromhex(
+        '6bc1bee2 2e409f96 e93d7e11 7393172a' +
+        'ae2d8a57 1e03ac9c 9eb76fac 45af8e51' +
+        '30c81c46 a35ce411 e5fbc119 1a0a52ef' +
+        'f69f2445 df4f9b17 ad2b417b e66c3710'
+    )
+    cmac = aes_cmac(m, k)
+    assert(cmac == bytes.fromhex('51f0bebf 7e3b9d92 fc497417 79363cfe'))
+
+
+# -----------------------------------------------------------------------------
+def test_f4():
+    u = bytes(reversed(bytes.fromhex(
+        '20b003d2 f297be2c 5e2c83a7 e9f9a5b9' +
+        'eff49111 acf4fddb cc030148 0e359de6'
+    )))
+    v = bytes(reversed(bytes.fromhex(
+        '55188b3d 32f6bb9a 900afcfb eed4e72a' +
+        '59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd'
+    )))
+    x = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab')))
+    z = bytes([0])
+    value = f4(u, v, x, z)
+    assert(bytes(reversed(value)) == bytes.fromhex('f2c916f1 07a9bd1c f1eda1be a974872d'))
+
+
+# -----------------------------------------------------------------------------
+def test_f5():
+    w = bytes(reversed(bytes.fromhex(
+        'ec0234a3 57c8ad05 341010a6 0a397d9b' +
+        '99796b13 b4f866f1 868d34f3 73bfa698'
+    )))
+    n1 = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab')))
+    n2 = bytes(reversed(bytes.fromhex('a6e8e7cc 25a75f6e 216583f7 ff3dc4cf')))
+    a1 = bytes(reversed(bytes.fromhex('00561237 37bfce')))
+    a2 = bytes(reversed(bytes.fromhex('00a71370 2dcfc1')))
+    value = f5(w, n1, n2, a1, a2)
+    assert(bytes(reversed(value[0])) == bytes.fromhex('2965f176 a1084a02 fd3f6a20 ce636e20'))
+    assert(bytes(reversed(value[1])) == bytes.fromhex('69867911 69d7cd23 980522b5 94750a38'))
+
+
+# -----------------------------------------------------------------------------
+def test_f6():
+    n1 = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab')))
+    n2 = bytes(reversed(bytes.fromhex('a6e8e7cc 25a75f6e 216583f7 ff3dc4cf')))
+    mac_key = bytes(reversed(bytes.fromhex('2965f176 a1084a02 fd3f6a20 ce636e20')))
+    r = bytes(reversed(bytes.fromhex('12a3343b b453bb54 08da42d2 0c2d0fc8')))
+    io_cap = bytes(reversed(bytes.fromhex('010102')))
+    a1 = bytes(reversed(bytes.fromhex('00561237 37bfce')))
+    a2 = bytes(reversed(bytes.fromhex('00a71370 2dcfc1')))
+    value = f6(mac_key, n1, n2, r, io_cap, a1, a2)
+    assert(bytes(reversed(value)) == bytes.fromhex('e3c47398 9cd0e8c5 d26c0b09 da958f61'))
+
+
+# -----------------------------------------------------------------------------
+def test_g2():
+    u = bytes(reversed(bytes.fromhex(
+        '20b003d2 f297be2c 5e2c83a7 e9f9a5b9' +
+        'eff49111 acf4fddb cc030148 0e359de6'
+    )))
+    v = bytes(reversed(bytes.fromhex(
+        '55188b3d 32f6bb9a 900afcfb eed4e72a' +
+        '59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd'
+    )))
+    x = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab')))
+    y = bytes(reversed(bytes.fromhex('a6e8e7cc 25a75f6e 216583f7 ff3dc4cf')))
+    value = g2(u, v, x, y)
+    assert(value == 0x2f9ed5ba)
+
+
+# -----------------------------------------------------------------------------
+def test_h6():
+    KEY = bytes.fromhex('ec0234a3 57c8ad05 341010a6 0a397d9b')
+    KEY_ID = bytes.fromhex('6c656272')
+    assert(h6(KEY, KEY_ID) == bytes.fromhex('2d9ae102 e76dc91c e8d3a9e2 80b16399'))
+
+
+# -----------------------------------------------------------------------------
+def test_h7():
+    KEY = bytes.fromhex('ec0234a3 57c8ad05 341010a6 0a397d9b')
+    SALT = bytes.fromhex('00000000 00000000 00000000 746D7031')
+    assert(h7(SALT, KEY) == bytes.fromhex('fb173597 c6a3c0ec d2998c2a 75a57011'))
+
+
+# -----------------------------------------------------------------------------
+def test_ah():
+    irk = bytes(reversed(bytes.fromhex('ec0234a3 57c8ad05 341010a6 0a397d9b')))
+    prand = bytes(reversed(bytes.fromhex('708194')))
+    value = ah(irk, prand)
+    expected = bytes(reversed(bytes.fromhex('0dfbaa')))
+    assert(value == expected)
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+    test_ecc()
+    test_c1()
+    test_s1()
+    test_aes_cmac()
+    test_f4()
+    test_f5()
+    test_f6()
+    test_g2()
+    test_h6()
+    test_h7()
+    test_ah()
diff --git a/tests/transport_test.py b/tests/transport_test.py
new file mode 100644
index 0000000..3005345
--- /dev/null
+++ b/tests/transport_test.py
@@ -0,0 +1,74 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import random
+import os
+from bumble.transport.common import PacketParser
+
+
+# -----------------------------------------------------------------------------
+class Sink:
+    def __init__(self):
+        self.packets = []
+
+    def on_packet(self, packet):
+        self.packets.append(packet)
+
+
+# -----------------------------------------------------------------------------
+def test_parser():
+    sink1 = Sink()
+    parser1 = PacketParser(sink1)
+    sink2 = Sink()
+    parser2 = PacketParser(sink2)
+
+    for parser in [parser1, parser2]:
+        with open(os.path.join(os.path.dirname(__file__), 'hci_data_001.bin'), 'rb') as input:
+            while True:
+                n = random.randint(1, 9)
+                data = input.read(n)
+                if not data:
+                    break
+                parser.feed_data(data)
+
+    assert(sink1.packets == sink2.packets)
+
+
+# -----------------------------------------------------------------------------
+def test_parser_extensions():
+    sink = Sink()
+    parser = PacketParser(sink)
+
+    # Check that an exception is thrown for an unknown type
+    try:
+        parser.feed_data(bytes([0x77, 0x00, 0x02, 0x01, 0x02]))
+        exception_thrown = False
+    except ValueError:
+        exception_thrown = True
+
+    assert(exception_thrown)
+
+    # Now add a custom info
+    parser.extended_packet_info[0x77] = (1, 1, 'B')
+    parser.reset()
+    parser.feed_data(bytes([0x77, 0x00, 0x02, 0x01, 0x02]))
+    assert(len(sink.packets) == 1)
+
+
+# -----------------------------------------------------------------------------
+test_parser()
+test_parser_extensions()
\ No newline at end of file
diff --git a/utils/generate_company_id_list.py b/utils/generate_company_id_list.py
new file mode 100644
index 0000000..bba42b8
--- /dev/null
+++ b/utils/generate_company_id_list.py
@@ -0,0 +1,38 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# This script generates a python-syntax list of dictionary entries for the
+# company IDs listed at: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/
+# The input to this script is the CSV file that can be obtained at that URL
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import sys
+import csv
+
+# -----------------------------------------------------------------------------
+with open(sys.argv[1], newline='') as csvfile:
+    reader = csv.reader(csvfile, delimiter=',', quotechar='"')
+    lines = []
+    for row in reader:
+        if len(row) == 3 and row[1].startswith('0x'):
+            company_id = row[1]
+            company_name = row[2]
+            escaped_company_name = company_name.replace('"', '\\"')
+            lines.append(f'    {company_id}: "{escaped_company_name}"')
+
+    print(',\n'.join(reversed(lines)))
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..c8c77e0
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,131 @@
+<html>
+  <head>
+    <script src="https://cdn.jsdelivr.net/pyodide/v0.19.1/full/pyodide.js"></script>
+  </head>
+
+  <body>
+    <button onclick="runUSB()">USB</button>
+    <button onclick="runSerial()">Serial</button>
+    <br />
+    <br />
+    <div>Output:</div>
+    <textarea id="output" style="width: 100%;" rows="30" disabled></textarea>
+
+    <script>
+        function bufferToHex(buffer) {
+            return [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, '0')).join('');
+        }
+
+        const output = document.getElementById("output");
+        const code = document.getElementById("code");
+
+        function addToOutput(s) {
+            output.value += s + "\n";
+        }
+
+      output.value = "Initializing...\n";
+
+      async function main() {
+          let pyodide = await loadPyodide({
+              indexURL: "https://cdn.jsdelivr.net/pyodide/v0.19.1/full/",
+          })
+          output.value += "Ready!\n"
+
+          return pyodide;
+      }
+
+      let pyodideReadyPromise = main();
+
+      async function readLoop(port, packet_source) {
+        const reader = port.readable.getReader()
+            try {
+                while (true) {
+                    console.log('@@@ Reading...')
+                    const { done, value } = await reader.read()
+                    if (done) {
+                        console.log("--- DONE!")
+                        break
+                    }
+
+                    console.log('@@@ Serial data:', bufferToHex(value))
+                    if (packet_source.delegate !== undefined) {
+                        packet_source.delegate.data_received(value)
+                    } else {
+                        console.warn('@@@ delegate not set yet, dropping data')
+                    }
+                }
+            } catch (error) {
+                console.error(error)
+            } finally {
+                reader.releaseLock()
+            }
+      }
+
+      async function runUSB() {
+        const device = await navigator.usb.requestDevice({
+          filters: [
+            {
+                classCode: 0xE0,
+                subclassCode: 0x01
+            }
+          ]
+        });
+
+        if (device.configuration === null) {
+          await device.selectConfiguration(1);
+        }
+        await device.claimInterface(0)
+      }
+
+      async function runSerial() {
+        const ports = await navigator.serial.getPorts()
+          console.log('Paired ports:', ports)
+
+          const port = await navigator.serial.requestPort()
+          await port.open({ baudRate: 1000000 })
+          const writer = port.writable.getWriter()
+      }
+
+      async function run() {
+
+          let pyodide = await pyodideReadyPromise;
+          try {
+              const script = await(await fetch('scanner.py')).text()
+              await pyodide.loadPackage('micropip')
+              await pyodide.runPythonAsync(`
+                  import micropip
+                  await micropip.install('../dist/bumble-0.0.36.dev0+g3adbfe7.d20210807-py3-none-any.whl')
+              `)
+              let output = await pyodide.runPythonAsync(script)
+              addToOutput(output)
+
+              const pythonMain = pyodide.globals.get('main')
+              const packet_source = {}
+              const packet_sink = {
+                  on_packet: (packet) => {
+                    // Variant A, with the conversion done in Javascript
+                      const buffer = packet.toJs()
+                      console.log(`$$$ on_packet: ${bufferToHex(buffer)}`)
+                      // TODO: create an sync queue here instead of blindly calling write without awaiting
+                      /*await*/ writer.write(buffer)
+                      packet.destroy()
+
+                    // Variant B, with the conversion `to_js` done at the Python layer
+                    // console.log(`$$$ on_packet: ${bufferToHex(packet)}`)
+                    //   /*await*/ writer.write(packet)
+                }
+              }
+              serialLooper = readLoop(port, packet_source)
+              pythonResult = await pythonMain(packet_source, packet_sink)
+              console.log(pythonResult)
+              serialResult = await serialLooper
+              writer.releaseLock()
+              await port.close()
+              console.log('### done')
+        } catch (err) {
+          addToOutput(err);
+        }
+      }
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/web/scanner.py b/web/scanner.py
new file mode 100644
index 0000000..9ab9f47
--- /dev/null
+++ b/web/scanner.py
@@ -0,0 +1,63 @@
+# Copyright 2021-2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from bumble.device import Device
+from bumble.transport import PacketParser
+
+
+# -----------------------------------------------------------------------------
+class ScannerListener(Device.Listener):
+    def on_advertisement(self, address, ad_data, rssi, connectable):
+        address_type_string = ('P', 'R', 'PI', 'RI')[address.address_type]
+        print(f'>>> {address} [{address_type_string}]: RSSI={rssi}, {ad_data}')
+
+
+class HciSource:
+    def __init__(self, host_source):
+        self.parser = PacketParser()
+        host_source.delegate = self
+
+    def set_packet_sink(self, sink):
+        self.parser.set_packet_sink(sink)
+
+    # host source delegation
+    def data_received(self, data):
+        print('*** DATA from JS:', data)
+        buffer = bytes(data.to_py())
+        self.parser.feed_data(buffer)
+
+
+# class HciSink:
+#     def __init__(self, host_sink):
+#         self.host_sink = host_sink
+
+#     def on_packet(self, packet):
+#         print(f'>>> PACKET from Python: {packet}')
+#         self.host_sink.on_packet(packet)
+
+
+# -----------------------------------------------------------------------------
+async def main(host_source, host_sink):
+    print('### Starting Scanner')
+    hci_source = HciSource(host_source)
+    hci_sink = host_sink
+    device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
+    device.listener = ScannerListener()
+    await device.power_on()
+    await device.start_scanning()
+
+    print('### Scanner started')