Apple TV & HomePod 1.5.1
- Fixed a bug that caused the “Companion link failed” trigger to fire multiple times. It now fires once per device.
macOS 26.3
And yes, I’m registered as admin.
@DirkG I’ve just uploaded a new signed version of the macOS versions.
Follow these steps after download:
@BasMilius, sorry, but still zsh: permission denied.
What I did:
./ap-diagnostics-macos-arm64 and confirmed the entry with the Enter key.What I’m doing wrong? ![]()
@DirkG Can you try running this command in the terminal and then run the tool?
chmod +x ./ap-diagnostics-macos-arm64
Everything else was spot on, except double clicking the binary won’t work at all ![]()
@BasMilius Any update /ETA of a possible future functionality to play “soundboard (app)” sounds on a HomePod (mini).
I remember you already mentioned anything about that but didn’t completely understand your answer then.
Does’t be fast or don’t want you to stress just politely and humbly asking for a possibility of a progress/ planning update.
Thanks.
Those people who use Ubiquiti/Unifi I would recommend to create a IP group (under profiles) of devices = IP addresses (for your Homey wifi & lan and your phone and other smarthome hubs and controllers) that has access to all ip’s/subnets & no restrictions at all by any firewall rules and devices not only initiate a connection but also the return connection from the contacted(by Homey) device.
My one of the highest top priority firewall rules in Unifi
Don’t forget to enable the “Auto Enable Return Traffic”.
My IP Group of local ip’s of Devices like Homey Pro and other Smarthome hubs/controllers (like Hue bridge and your Phone & laptops) that should have all access (internal/local) at least if not completely everywhere :
Its not necessary to add the End-Devices to this list.
Although I added my 2 AppleTV because they are my HomeKit controllers and so must be able to connect all my devices.
And specify your internal Unfi gateway IP adress as 1st DNS server to use by all your internal devices through your DHCP settings or manual IP settings of your devices (and the the 2nd DNS I prefer snd recommend is 9.9.9.9).
Nothing happens… ![]()
That’s what I assumed, actually. But without opening the tool in Finder, there was no corresponding entry under “Privacy and Security.” See no. 4 in my post #310.
@DoctorBazinga, Thank you for the comment, but I don’t quite understand what you mean, or rather, it’s beyond my knowledge.
Do you use Ubiquiti hardware with a Gateway with Unifi network (firewall on it ) ?
If so. The question is if some connection / datatransfer between your Homey and your apple device is not filtered or completely disabled by some (simple/basic) firewall rules.
We could talk in more private in a DM about it if you’re interested. Bu I need some specifics of your network hardware etc then.
I think you still don’t have or are not in the state of full access /all (admin permissions) on your mac-book beeing in full administratieve rights to run possibly harmful/privacy concerning content.
I’m not a apple MacBook user/expert. But had sometimes to help some elderly people who had a MacBook and its quite difficult to gain full access & control as administrator to be able to run/install anything you please on a MacBook nowadays. Had to fill in so much local an apple iCloud accounts & passwords. (A nightmare for me as a windows guy).
I think you still lack the full ability/access power on your MacBook to run these kind of (terminal) scripts.
Otherwise it your network (firewall) as I mentioned before that could prevent something.
Just googled it and this was Google’s /Gemini’s answers:
Gaining full permission on macOS involves
elevating user privileges to Administrator, granting applications “Full Disk Access,” and sometimes utilizing terminal commands for system-wide read/write capabilities
. Modern macOS (Mojave and later) restricts third-party apps from accessing sensitive user data, requiring manual approval.
Salesforce +3
Here is a guide to gaining full permissions on macOS:
1. Granting Full Disk Access (System Settings)
This allows applications to access protected files like Mail, Messages, Safari, and Time Machine backups.
N-able
Open System Settings/Preferences: Go to the Apple menu > System Settings > Privacy & Security.
Select Full Disk Access: Scroll down and click Full Disk Access.
Unlock Changes: Click the padlock icon or use Touch ID to make changes, entering your administrator password.
Add the Application: Click the + button, locate the application, and click Open.
Toggle On: Ensure the toggle next to the application is turned on.
YouTube +4
2. Giving an App Accessibility/Automation Access
Apps that control your Mac (like automation tools) require explicit permission.
Go to System Settings > Privacy & Security.
Select Accessibility or Automation.
Toggle on the app requesting control.
Apple Support +1
3. Elevating a User Account to Administrator
If you are a standard user, you need administrator rights to install apps and change system settings.
Go to System Settings > Users & Groups.
Click the “i” icon next to your username.
Enable Allow this user to administer this computer.
Restart your Mac to apply the changes.
ExamSoft +2
4. Changing File/Folder Permissions via Finder
If you cannot edit a specific file or folder, you can override permission restrictions:
Select the item in Finder and press Command + I (or File > Get Info).
Click the arrow next to Sharing & Permissions.
Click the lock icon and enter your password.
Change the privilege for your user account to Read & Write.
To apply to all enclosed items, click the action menu (…) and select Apply to enclosed items.
Apple Support +4
5. Using Terminal for Elevated Access (sudo)
To perform actions requiring root privileges in the terminal, use the sudo command.
Open Terminal and type sudo [command].
Enter your admin password when prompted.
Note: If you want to log in as root (not recommended), use sudo -s to start a root shell.
Apple Support +2
6. Fixing “Read-Only” Drives
If an external drive is read-only, you can override this:
Select the drive in Finder and press Command + I.
Under Sharing & Permissions, check the box for “Ignore ownership on this volume”.
Setapp +1
Important Security Note: Granting Full Disk Access gives apps unrestricted access to your personal data. Only enable this for applications you know and trust.
@DirkG Did you try and run the tool after that? The chmod command isn’t supose to output something for this use case ![]()
✔ What would you like to test? · appletv-companion-link-verify
If your device is not shown, restart the diagnostics tool and try again.
✔ Found 1 Companion Link devices
✔ Which device would you like to verify? · Woonkamer.local
Device Woonkamer._companion-link._tcp.local (Woonkamer.local) is not supported, only Apple TV's can be verified.
Done
nxxx@MB-F6HR9CQQ6H ~/Downloads ./ap-diagnostics-macos-arm64
Welcome to the diagnostics tool for Apple Protocols.
With this tool, all traffic between the library and your Apple devices is being logged. Please only share the logs with Bas when asked, as the log contains encryption keys.
Press Control-C to exit.
✔ What would you like to test? · appletv-launch-app
If your device is not shown, restart the diagnostics tool and try again.
✔ Found 1 Companion Link devices
✔ Which device would you like to verify? · Woonkamer.local
Device Woonkamer._companion-link._tcp.local (Woonkamer.local) is not supported, only Apple TV's can be verified.
Done
@nielsoet What model do you have? And can you also try airplay pairing? It will probably also not work, but maybe..
That one works! Just share the output here?
Edit: Throws an error so let’s just share!
Welcome to the diagnostics tool for Apple Protocols.
With this tool, all traffic between the library and your Apple devices is being logged. Please only share the logs with Bas when asked, as the log contains encryption keys.
Press Control-C to exit.
✔ What would you like to test? · airplay-pair
If your device is not shown, restart the diagnostics tool and try again.
✔ Found 3 AirPlay devices
✔ Which device would you like to pair? · Woonkamer.local
[net] [Woonkamer.local] Connecting to 192.168.2.95:7000...
[net] [Woonkamer.local] [control] POST /pair-pin-start cseq = 0
604 | `),F=$[0].match(/^(\S+)\s+(\S+)\s+RTSP\/1\.0$/),N=F[1],K=F[2];return{headers:LX($.slice(1)),method:N,path:K}}function dq(A){let $=A.toString("utf8").split(`\r
605 | `),F=$[0].match(/(HTTP|RTSP)\/[\d.]+\s+(\d+)\s+(.+)/),N=Number(F[2]),K=F[3];return{headers:LX($.slice(1)),status:N,statusText:K}}var B0={};F8(B0,{encode:()=>FW,decode:()=>NW,bail:()=>$W,Value:()=>Z9,State:()=>AW,Method:()=>eq,Flags:()=>aq,ErrorCode:()=>UX});var aq={TransientPairing:16},UX={Unknown:1,Authentication:2,BackOff:3,MaxPeers:4,MaxTries:5,Unavailable:6,Busy:7},eq={PairSetup:0,PairSetupWithAuth:1,PairVerify:2,AddPairing:3,RemovePairing:4,ListPairing:5},AW={M1:1,M2:2,M3:3,M4:4,M5:5,M6:6},Z9={Method:0,Identifier:1,Salt:2,PublicKey:3,Proof:4,EncryptedData:5,State:6,Error:7,BackOff:8,Certificate:9,Signature:10,Permissions:11,FragmentData:12,FragmentLast:13,Name:17,Flags:19};function $W(A){if(A.has(Z9.BackOff)){let $=A.get(Z9.BackOff),F=$.readUintLE(0,$.length);throw Error(`Device is busy, try again in ${F} seconds.`)}if(A.has(Z9.Error)){let F=Object.entries(UX).find(([N,K])=>K===A.get(Z9.Error).readUint8());if(!F)throw Error(`Device returned an unknown error code: ${A.get(Z9.Error).readUint8()}`);throw Er | ... truncated
606 | `)},w=(...J)=>{UK.write(tU(uU(...J))+`
607 | `)};console.log=(...J)=>{G(...J),A(...J)},console.debug=(...J)=>{G(...J),$(...J)},console.info=(...J)=>{G(...J),F(...J)},console.warn=(...J)=>{w(...J),N(...J)},console.error=(...J)=>{w(...J),K(...J)}}function $w(){LK?.close(),UK?.close()}function PF(A,$){if(!(A!==null&&typeof A=="object"&&("$typeName"in A)&&typeof A.$typeName=="string"))return!1;if($===void 0)return!0;return $.typeName===A.$typeName}var z;(function(A){A[A.DOUBLE=1]="DOUBLE",A[A.FLOAT=2]="FLOAT",A[A.INT64=3]="INT64",A[A.UINT64=4]="UINT64",A[A.INT32=5]="INT32",A[A.FIXED64=6]="FIXED64",A[A.FIXED32=7]="FIXED32",A[A.BOOL=8]="BOOL",A[A.STRING=9]="STRING",A[A.BYTES=12]="BYTES",A[A.UINT32=13]="UINT32",A[A.SFIXED32=15]="SFIXED32",A[A.SFIXED64=16]="SFIXED64",A[A.SINT32=17]="SINT32",A[A.SINT64=18]="SINT64"})(z||(z={}));function pU(){let A=0,$=0;for(let N=0;N<28;N+=7){let K=this.buf[this.pos++];if(A|=(K&127)<<N,(K&128)==0)return this.assertBounds(),[A,$]}let F=this.buf[this.pos++];if(A|=(F&15)<<28,$=(F&112)>>4,(F&128)==0)return this.assertBounds(),[A,$]; | ... truncated
608 | `:case"\r":case"\t":case" ":continue;default:throw Error("invalid base64 string")}switch(G){case 0:J=w,G=1;break;case 1:N[K++]=J<<2|(w&48)>>4,J=w,G=2;break;case 2:N[K++]=(J&15)<<4|(w&60)>>2,J=w,G=3;break;case 3:N[K++]=(J&3)<<6|w,G=0;break}}if(G==1)throw Error("invalid base64 string");return N.subarray(0,K)}var OK,IZ,T9;function y7(A){if(!OK)OK="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),IZ=OK.slice(0,-2).concat("-","_");return A=="url"?IZ:OK}function n7(){if(!T9){T9=[];let A=y7("std");for(let $=0;$<A.length;$++)T9[A[$].charCodeAt(0)]=$;T9[45]=A.indexOf("+"),T9[95]=A.indexOf("/")}return T9}function j9(A){let $=!1,F=[];for(let N=0;N<A.length;N++){let K=A.charAt(N);switch(K){case"_":$=!0;break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":F.push(K),$=!1;break;default:if($)$=!1,K=K.toUpperCase();F.push(K);break}}return F.join("")}var v7=new Set(["constructor","toString","toJSON","valueOf"]);function D9(A){return v7.has(A)?A+"$":A}function EF(A){fo | ... truncated
609 | `),K;if(A.body)K=Buffer.concat([Buffer.from(N),$]);else K=Buffer.from(N);if(this.isEncrypted)K=this.encrypt(K);this.write(K)}setup(A){let $=v1({hash:"sha512",key:A,length:32,salt:Buffer.from("Events-Salt"),info:Buffer.from("Events-Read-Encryption-Key")}),F=v1({hash:"sha512",key:A,length:32,salt:Buffer.from("Events-Salt"),info:Buffer.from("Events-Write-Encryption-Key")});this.enableEncryption(F,$)}#$(){this.#F=Buffer.alloc(0)}async#N(A,$,F,N){let K=`${A} ${$}`;switch(K){case"POST /command":let G=B1.parse(N.buffer.slice(N.byteOffset,N.byteOffset+N.byteLength));this.context.logger.info("[event]","Received event stream request.",G);let w=new Response(null,{status:200,statusText:"OK",headers:{"Audio-Latency":"0",CSeq:(F.CSeq??0).toString()}});await this.respond(w);break;default:this.context.logger.warn("[event]","No handler for url",K);break}}#A(){this.#$()}async#G(A){try{if(this.#F=Buffer.concat([this.#F,A]),this.isEncrypted){let $=this.decrypt(this.#F);if(!$)return;this.#F=$}while(this.#F.byteLength>0){let $=XA. | ... truncated
error: Cannot start pairing session. 403 Forbidden
at #A (/$bunfs/root/index.js:609:1782)
at async pin (/$bunfs/root/index.js:609:1485)
at async zJ (/$bunfs/root/index.js:620:2800)
Bun v1.3.9 (macOS arm64)
Sorry to be the messenger of bad news but somehow I lost connection to all my devices since the last version of your app I’ve installed 17 hours ago. ![]()
Restart your app or even my Homey didn’t solve the problem.
At least 2 HomePod mini’s should be available. The AppleTV’s might be disconnected from power though a smarthome power plug an the 3rd I’ve disconnected manualy because the sudden autoplay.
Diagnostics report :
1a20f34c-2ee7-4ee3-a9dc-90fcc7ebbe3e
Oh great, it fails at the point where I just ask if I can pair… What Apple TV model do you have exactly?
@DoctorBazinga Weird, just like last time I cannot find your diagnostics report. I see that you are on the latest version and I haven’t received any reports on that version. Do you have a firewall active or something that is blocking these reports? ![]()
What do the devices say when you open them?
Really weird, yes. It’s the latest Atv 4K model, with Ethernet.
After what?
@DoctorBazinga, adding the diagnostic tool manually for full hard disk access was the key. Thank you very much!
EDIT
@BasMilius, here is the log file from my Apple HomePod mini:
Error log
[error] [Wohnzimmer.local] Connection error: connect ECONNREFUSED 192.168.1.77:53754
[error] [Wohnzimmer.local] [control] #onError() Error: connect ECONNREFUSED 192.168.1.77:53754
at afterConnect (node:net:1153:39)
at connectError (node:net:350:48) {
errno: -61,
syscall: 'connect',
port: 53754,
address: '192.168.1.77',
code: 'ECONNREFUSED'
}
[error] [Wohnzimmer.local] Connection error: connect ECONNREFUSED 192.168.1.77:53754
[error] [Wohnzimmer.local] [control] #onError() Error: connect ECONNREFUSED 192.168.1.77:53754
at afterConnect (node:net:1153:39)
at connectError (node:net:350:48) {
errno: -61,
syscall: 'connect',
port: 53754,
address: '192.168.1.77',
code: 'ECONNREFUSED'
}
[error] [Wohnzimmer.local] Connection error: connect ECONNREFUSED 192.168.1.77:53754
[error] [Wohnzimmer.local] [control] #onError() Error: connect ECONNREFUSED 192.168.1.77:53754
at afterConnect (node:net:1153:39)
at connectError (node:net:350:48) {
errno: -61,
syscall: 'connect',
port: 53754,
address: '192.168.1.77',
code: 'ECONNREFUSED'
}
[error] [Wohnzimmer.local] Connection error: connect ECONNREFUSED 192.168.1.77:53754
[error] [Wohnzimmer.local] [control] #onError() Error: connect ECONNREFUSED 192.168.1.77:53754
at afterConnect (node:net:1153:39)
at connectError (node:net:350:48) {
errno: -61,
syscall: 'connect',
port: 53754,
address: '192.168.1.77',
code: 'ECONNREFUSED'
}
Output log
[net] [Wohnzimmer.local] Connecting to 192.168.1.77:53754...
[net] [Wohnzimmer.local] Retry attempt 1 / 3 in 3000ms...
[net] [Wohnzimmer.local] Connecting to 192.168.1.77:53754...
[net] [Wohnzimmer.local] Retry attempt 2 / 3 in 3000ms...
[net] [Wohnzimmer.local] Connecting to 192.168.1.77:53754...
[net] [Wohnzimmer.local] Retry attempt 3 / 3 in 3000ms...
[net] [Wohnzimmer.local] Connecting to 192.168.1.77:53754...
[net] [Wohnzimmer.local] Connection closed (with error).
[net] [Wohnzimmer.local] [control] #onClose()