sync: auto-sync from GURU-5070 at 2026-06-22 10:03:54
Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-06-22 10:03:54
This commit is contained in:
125
session-logs/2026-06/2026-06-22-mike-yealink-ymcs-skill.md
Normal file
125
session-logs/2026-06/2026-06-22-mike-yealink-ymcs-skill.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Session — yealink-ymcs skill built (YMCS v2 API), pairs with packetdial; vendor-stack correction
|
||||
|
||||
## User
|
||||
- **User:** Mike Swanson (mike)
|
||||
- **Machine:** GURU-5070
|
||||
- **Role:** admin
|
||||
|
||||
## Session Summary
|
||||
|
||||
Continuation of the VoIP onboarding work. Kept the `vwp.91912.service` domain created last
|
||||
session and fixed its `domain-type` to `Standard` (the create had stored `"no"`). Then built the
|
||||
`onboard-domain` wrapper into the packetdial skill — one gated command running the GUI Add-a-Domain
|
||||
flow as 3 API calls (POST /domains → addresses/validate → addresses/create), verified end-to-end
|
||||
with a throwaway create→delete. Mike then clarified the vendor stack (PacketDial = ACG's VoIP-dept
|
||||
brand; NetSapiens = the platform; OIT/OITVOIP = white-label wholesaler) — captured as a reference
|
||||
memory + logged correction.
|
||||
|
||||
Mike's main ask: add the related service — **Yealink cloud device manager** — and make sure we have
|
||||
access + can use it with the PBX skill. Discovery found ACG already has a Yealink admin
|
||||
(`admin@azcomputerguru.com`, vault `infrastructure/voip-phones.sops.yaml`) and that the cloud
|
||||
manager is **YMCS** (`us.ymcs.yealink.com`); the wiki documents VWP's Yealink T54W fleet. Mike then
|
||||
supplied **YMCS API credentials** (AccessKey ID + Secret) via screenshot, which were vaulted at
|
||||
`services/yealink-ymcs.sops.yaml`.
|
||||
|
||||
On Mike's go, RTFM'd the YMCS open API and built a new **`yealink-ymcs` skill**. The current v2 API
|
||||
uses token auth (`POST /v2/token` Basic → bearer), not the legacy RPS HMAC signing; Yealink servers
|
||||
require legacy TLS renegotiation. Harvested the full `/v2/dm/*` + `/v2/rps/*` endpoint map from the
|
||||
`dszp/n8n-nodes-yealinkymcs` community node. Implemented `ymcs_client.py` (token+bearer, legacy-TLS
|
||||
SSL context, paginated `_list`, vault creds) + `ymcs.py` CLI (reads + gated writes + raw). Live-
|
||||
verified: token auth, sites, devices (21), accounts, rps-servers. **The ACG AccessKey is the parent
|
||||
account that sees ALL client sites** (VWP, GuruHQ, Ace Pick Up Parks as children) — so one key
|
||||
covers every client fleet; answers Mike's per-client-vs-one-key question.
|
||||
|
||||
The pbx+phone pipeline now exists end to end: `packetdial onboard-domain` (PBX domain + SIP
|
||||
accounts) → `yealink-ymcs` (`add-devices-by-mac`, `add-sipaccount` to push a NetSapiens SIP cred
|
||||
onto a phone, `rps-add` for zero-touch via RPS server `WL - ACG` = `ftp://p.packetdials.net`). VWP
|
||||
is the live pilot (onboarded to the PBX; 21 devices in YMCS, fleet partly pending). Everything
|
||||
committed, pushed, deployed.
|
||||
|
||||
## Key Decisions
|
||||
- **Token auth, not legacy HMAC.** The first sources described the old RPS Management Platform HMAC
|
||||
signing (X-Ca-* + Content-MD5); the n8n node revealed the current v2 API uses `POST /v2/token`
|
||||
(Basic) → bearer. Built to v2 (simpler, current).
|
||||
- **Legacy TLS handled in-client** via an SSL context with `OP_LEGACY_SERVER_CONNECT` (Yealink
|
||||
servers require renegotiation; default Python TLS would fail).
|
||||
- **One AccessKey for all clients** — verified the key is the ACG parent account; client sites are
|
||||
children. No per-client API keys.
|
||||
- **`add-sipaccount` instead of a sip-account list.** `/v2/dm/sipAccounts` (POST) is the CREATE
|
||||
endpoint (not a list); wrapped it as the gated write since pushing a SIP cred onto a phone is the
|
||||
actual PBX↔phone integration glue. No list-sip-accounts exists in v2.
|
||||
- **Read-first, writes gated --confirm** (mirrors packetdial); `raw` passthrough for the rest.
|
||||
- **Honest [V]/[P] status in docs** — only token/sites/devices/accounts/rps-servers lifecycle-
|
||||
verified; writes + official-firmwares + `--site` filter are plumbed-but-unverified (no throwaway
|
||||
device to safely test writes on).
|
||||
|
||||
## Problems Encountered
|
||||
- **MSYS path conversion on `raw`:** a leading `/v2/...` arg gets rewritten by Git-Bash to
|
||||
`C:/Program Files/Git/v2/...`. Added in-code recovery (cut back to `/v2/`). `MSYS_NO_PATHCONV=1`
|
||||
is NOT a fix — it broke the vault subprocess (creds failed). Named wrappers (hardcoded paths)
|
||||
are unaffected. (Documented Windows gotcha class.)
|
||||
- **`/v2/dm/sipAccounts` is a create, not a list** — my initial `sipaccounts` read wrapper hit it
|
||||
and got HTTP 400 (`sipServer1 cannot be null`). Replaced with the gated `add-sipaccount`.
|
||||
- **`official-firmwares`** returns a non-standard shape (likely needs a model param) — marked [P].
|
||||
- **`--site` filter on `devices`** returned all 21 (siteId in the body didn't scope) — best-effort,
|
||||
marked [P]; full data still returned.
|
||||
- **cp1252 + WebFetch 403** while RTFMing — used `encoding='utf-8'` for spec reads and pivoted from
|
||||
the 403'd Yealink doc to the public GitHub n8n node source via the GitHub API + raw files.
|
||||
- **`domain-type` stored `"no"` on packetdial create** — fixed vwp via `raw PUT` (carryover).
|
||||
|
||||
## Configuration Changes
|
||||
Created:
|
||||
- `.claude/skills/yealink-ymcs/SKILL.md`, `scripts/ymcs.py`, `scripts/ymcs_client.py` (new skill).
|
||||
- `services/yealink-ymcs.sops.yaml` (VAULT) — YMCS API AccessKey.
|
||||
- `.claude/memory/reference_packetdial_oit_netsapiens.md` (+ MEMORY.md index line).
|
||||
- This session log.
|
||||
Modified:
|
||||
- `.claude/skills/packetdial/SKILL.md`, `scripts/ns.py`, `scripts/ns_client.py` — `onboard-domain`
|
||||
wrapper.
|
||||
- `errorlog.md` — vendor-model correction.
|
||||
Live changes: vwp `domain-type` → Standard; threw away `acg-onbtest.91912.service` (wrapper test).
|
||||
|
||||
## Credentials & Secrets
|
||||
- **YMCS API AccessKey** — vaulted `services/yealink-ymcs.sops.yaml`:
|
||||
`credentials.access_key_id` / `credentials.access_key_secret`. Region us
|
||||
(`us-api.ymcs.yealink.com`). Read with `vault.sh get-field services/yealink-ymcs.sops.yaml
|
||||
credentials.access_key_id`. (Values intentionally not echoed here — they're vaulted.)
|
||||
- Existing: Yealink portal admin `admin@azcomputerguru.com` → `infrastructure/voip-phones.sops.yaml`;
|
||||
per-client portal logins e.g. `clients/valleywide/`. PBX reseller key `msp-tools/oitvoip.sops.yaml`.
|
||||
|
||||
## Infrastructure & Servers
|
||||
- YMCS API: `https://us-api.ymcs.yealink.com` (v2). Auth `POST /v2/token` Basic→bearer; legacy TLS.
|
||||
- YMCS account tree (ACG parent key): `Arizona Computer Guru LLC` (id 34a602e7…) → `VWP`
|
||||
(1e7578a6…), `GuruHQ` (04117c54…), `Ace Pick Up Parks` (0320c810…).
|
||||
- RPS server: `WL - ACG` (id 019c908f…), url `ftp://p.packetdials.net`, authName `lrshwh`.
|
||||
- Device object keys: id, mac, sn, name, modelId/modelName, siteId/siteName, programVersion,
|
||||
deviceStatus, connectWay, wanIp, lanIp, lastReportTime, createTime. 21 devices total.
|
||||
- PBX: `pbx.packetdial.com/ns-api/v2` (NetSapiens via OIT); domain `vwp.91912.service` live.
|
||||
|
||||
## Commands & Outputs
|
||||
- YMCS reads: `ymcs.py sites|devices|accounts|rps-servers|firmwares|models|alarms|oplogs|device-groups|device-configs`
|
||||
- YMCS gated writes: `ymcs.py add-devices-by-mac|add-sipaccount|reboot|reset|rps-add|rps-del --body '{...}' --confirm`
|
||||
- `ymcs.py raw POST /v2/dm/listDevices --body '{"skip":0,"limit":50,"autoCount":true}'` (path auto-recovers MSYS mangling)
|
||||
- packetdial onboarding: `ns.py onboard-domain --body-file new-client.json --confirm`
|
||||
- vwp fix: `ns.py raw PUT domains/vwp.91912.service --body '{"domain-type":"Standard"}' --confirm`
|
||||
|
||||
## Pending / Incomplete Tasks
|
||||
- **[P] YMCS writes unverified** — add-devices-by-mac, add-sipaccount, reboot, reset, rps add/del;
|
||||
official-firmwares shape; `--site` device filter. Exercise on real devices (VWP's pending fleet)
|
||||
when a task needs them.
|
||||
- **Proposed (not started):** a combined `onboard-client` flow = packetdial onboard-domain +
|
||||
users/devices + YMCS phone provisioning (add-by-mac + add-sipaccount/rps) in one gated run,
|
||||
piloted on VWP's 11 pending phones.
|
||||
- **packetdial gaps (carryover):** voicemail user-defaults (not in POST /domains), `email-send-from-address`
|
||||
pending `voicemail@`/`noreply@packetdial.com` mailbox creation.
|
||||
- `OIT-API.txt` plaintext in Downloads — Mike to delete.
|
||||
|
||||
## Reference Information
|
||||
- New skill: `.claude/skills/yealink-ymcs/`. Vendor stack memory: `reference_packetdial_oit_netsapiens`.
|
||||
- Endpoint map source: `github.com/dszp/n8n-nodes-yealinkymcs` (n8n community node) + Yealink "Open
|
||||
API for YMCS V4X" doc. Auth doc patterns: support.yealink.com (legacy RPS HMAC — superseded by v2 token).
|
||||
- Commits this session: `onboard-domain` `d1d1302d`; vendor-stack memory `1dbefd54`; yealink-ymcs
|
||||
skill `850e685d`. Vault: oitvoip key + YMCS key pushed.
|
||||
- YMCS endpoints: `/v2/token`, `/v2/dm/{listSites,listDevices,listAccounts,sipAccounts,addDevicesByMac,
|
||||
addDevices,delDevices,device/reboot,device/reset,listFirmwares,models,...}`, `/v2/rps/{listServers,
|
||||
listDevices,addDevices,deleteDevices}`.
|
||||
Reference in New Issue
Block a user