blitzkopf / naim_muso

Home Assistant integration for Naim Mu-So media player
MIT License
2 stars 0 forks source link

Mu-so2 RESTful commands to set input and to manage power #31

Closed Lupi00 closed 1 month ago

Lupi00 commented 2 months ago

Hello.

I'll just start by thanking you for your work and say that I appreciate that someone is trying to help us naim users!

Is there a documentation somewhere that explains what functions that are working now?

I have a chromecast with google tv and I want to have the naim muso start whenever Home Assistant registers that the google tv starts up.

Right now I have an automation that makes the muso start up whenever the google tv starts, but it starts with the chromecast source selected. I want to have it start with the Digital source selected. Is it possible to select that source on start-up or switch to it as part of the automation?

blitzkopf commented 2 months ago

You can select the Digital input in a script using the media_player.select_source, something like this ( your entity id will be different )

  - service: media_player.select_source
    data:
      source: Digital
    entity_id: media_player.stofan

It is also possible to select the input by defining a Scene in the UI.

And you can also select the input source by clicking the three dots on on device in the dashboard, it might take a few seconds to populate the available sources after turning the device on.

Lupi00 commented 2 months ago

Seems that I cant get HA to change the source as you posted. I can turn on the muso with this yaml.

service: media_player.turn_on
data: {}
target:
  device_id: X

But if I configure it to change source it doesn't work.

service: media_player.select_source
data:
 source: Digital
target:
 device_id: X

Any reason why this doesn't work?

blitzkopf commented 2 months ago

I just connected my Mu-so, I'm renovating and had disconnected it. I made a script and a scene that actually works, my last answer was just guessing. There seems to be an issue with using device_id, it might be conflicting with DLNA media player, so I use the entity_id.

alias: Stofan Digital script
sequence:
  - action: media_player.turn_on
    metadata: {}
    data: {}
    target:
      entity_id: media_player.stofan_2
  - action: media_player.select_source
    metadata: {}
    data:
      source: Digital
    target:
      entity_id: media_player.stofan_2
description: ""
icon: mdi:toslink

As far as I can tell "action" is just the new name for "service".

And here is a scene that does the same also using the entity_id:

- id: '1723583194866'
  name: Stofan Digital
  entities:
    media_player.stofan_2:
      friendly_name: Stofan
      supported_features: 135044
      entity_picture_local:
      source_list:
      - iRadio
      - Multiroom
      - UPnP
      - USB/iPod
      - Bluetooth
      - Airplay
      - Spotify
      - TIDAL
      - Analogue
      - Digital
      volume_level: 0.14
      source: Digital
      state: 'on'
  icon: mdi:toslink
  metadata:
    media_player.stofan_2:
      entity_only: true

Just to satisfy my curiosity which type of device to you have, Mu-so 1 or 2 or QB? I've only ever tried to run it on Mu-so version 1.

Lupi00 commented 2 months ago

Hello again.

Neither way to select source seems to work for me. I have a mu-so gen 2.

I have tried all kind of combinations in service tab under the developers options and nothing seem to work.

For example

service: media_player.select_source
metadata: {}
data:
  source: Digital
target:
  entity_id: media_player.vardagsrum_speaker_2

It seems that the integration has created 3 different entities and frankly Im confused to which is the one I should work with.

image

When I look into the states in developers options I get this.

1 unavailable

friendly_name: Vardagsrum speaker
supported_features: 152461

2 idle

sound_mode_list:
  - FactoryDefaults
volume_level: 0.28
is_volume_muted: false
media_content_id: ""
media_duration: 0
media_track: 1
shuffle: false
repeat: "off"
friendly_name: Vardagsrum speaker
supported_features: 479807

3 unavailable

restored: true
friendly_name: Vardagsrum speaker
supported_features: 0

Also My media_player_turn_on automation works with an device_id but the source that is selected is Cast instead of Digital or the last used source.

My guess is that Im stuck with some simple fault that I just cant find.

Any suggestions on how I can move forward with this?

blitzkopf commented 2 months ago

If you look at the third column of entities list you can see that the three entities are created by three different integrations: Google Cast, DLNA Digital Media Renderer and naim Mu-so controller. The naim Mu-so is actually media_player.vardagsrum_speaker_3. This is because they are all three identifying the same device id. I know this is confusing, mine only registers 2 entities as DLNA and mu-so, generation 1 does not support Google Cast, but still I get confused.

I guess it is time we try too look at the Debug log, under Settings -> Integraions select naim Mu-so and click on "enable debug logging". Then hit the three dots on the right and select "reload", Wait half a minute and then click "1 entity" and you should see a list with only the naim Mu-so integration entitiy. Try hitting the ON-OFF button and wait 15 seconds, see if the "Source" list gets populated. It looks like this for me: image

You can then try to run your script.

Finally go back into Integrations and clock on "disable debug logging" you will be offered to download the log, do that and then attach the log to this issue.

Lupi00 commented 2 months ago

Thank you for clarify which entity I should focus on.

This is what I get

image

The log shows that there is something wrong when the integration tries to load

          ^^^^^^^^^^^^^^^
AttributeError: 'Controller' object has no attribute 'connection'. Did you mean: 'connect'?
2024-08-21 08:22:46.891 ERROR (MainThread) [homeassistant.helpers.entity] Update for media_player.vardagsrum_speaker_3 fails
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 951, in async_update_ha_state
    await self.async_device_update()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 1268, in async_device_update
    await self.async_update()
  File "/config/custom_components/naim_muso/media_player.py", line 470, in async_update
    await self._device.controller.send_command("GetViewState")
  File "/usr/local/lib/python3.12/site-packages/naimco/controllers.py", line 259, in send_command
    await self.connection.send(cmd)
          ^^^^^^^^^^^^^^^
blitzkopf commented 2 months ago

It is quite possible that generation 2 is using a different port or a totally different protocol. If I remember correctly from tracing IP traffic from the app it tries to open up another port which my Mu-so gen 1 rejects.

One thing to be aware of is that the Mu-so only allows one incoming connection so you might need to turn of the app on your phone.

It would be best if you sent the whole log, but anyway the log should include is something like this:

2024-08-20 21:27:39.425 INFO (MainThread) [homeassistant.setup] Setting up naim_muso
2024-08-20 21:27:39.426 INFO (MainThread) [homeassistant.setup] Setup of domain naim_muso took 0.0 seconds
2024-08-20 21:27:39.515 INFO (MainThread) [homeassistant.components.media_player] Setting up naim_muso.media_player
2024-08-20 21:27:39.516 DEBUG (MainThread) [custom_components.naim_muso] media_player.async_setup_entry 4d78ae6735e992a23ba655b643a7b0c1 (Stofan)
2024-08-20 21:27:39.517 DEBUG (MainThread) [custom_components.naim_muso] Connecting to device at http://192.168.1.145:8080/description.xml
2024-08-20 21:27:39.768 DEBUG (MainThread) [naimco.core] Created NaimCo instance for ip: 192.168.1.145
2024-08-20 21:27:39.768 DEBUG (MainThread) [naimco.core] Starting up NaimCo instance for ip: 192.168.1.145
2024-08-20 21:27:39.768 INFO (MainThread) [naimco.controllers] Starting up controller
2024-08-20 21:27:39.769 DEBUG (MainThread) [naimco.connection] Connecting to  Naim Mu-So on ip: 192.168.1.145
2024-08-20 21:27:39.852 DEBUG (MainThread) [naimco.msg_processing] recursing this {'item': {'name': 'module', 'string': 'NAIM'}}
2024-08-20 21:27:39.852 DEBUG (MainThread) [naimco.msg_processing] recursing this {'item': {'name': 'version', 'string': '1'}}
2024-08-20 21:27:39.853 DEBUG (MainThread) [naimco.connection] Send: '<command><name>RequestAPIVersion</name><id>1</id><map><item><name>module</name><string>NAIM</string></item><item><name>version</name><string>1</string></item></map></command>'
2024-08-20 21:27:39.854 DEBUG (MainThread) [naimco.connection] Send: '<command><name>GetBridgeCoAppVersions</name><id>2</id></command>'
2024-08-20 21:27:39.870 DEBUG (MainThread) [naimco.connection] Send: '<command><name>GetViewState</name><id>3</id></command>'
2024-08-20 21:27:39.870 DEBUG (MainThread) [naimco.controllers] Sending *NVM GETVIEWSTATE
2024-08-20 21:27:39.870 DEBUG (MainThread) [naimco.msg_processing] recursing this {'item': {'name': 'data', 'base64': 'Kk5WTSBHRVRWSUVXU1RBVEUN\n'}}
2024-08-20 21:27:39.870 DEBUG (MainThread) [naimco.connection] Send: '<command><name>TunnelToHost</name><id>4</id><map><item><name>data</name><base64>Kk5WTSBHRVRWSUVXU1RBVEUN\n</base64></item></map></command>'
2024-08-20 21:27:39.870 DEBUG (MainThread) [naimco.controllers] Sending *NVM GETPREAMP

The interesting lines here are Connecting to device at http://192.168.1.145:8080/description.xml and Connecting to Naim Mu-So on ip: 192.168.1.145.

Lupi00 commented 2 months ago

home-assistant_naim_muso_2024-08-21T15-05-38.004Z.log

More logs. I reloaded the naim muso integration and turned the mu-so2 off and on (via remote control) 21/8 at 17.00.

Nothing that you refer to in your logs are present in my logs.

blitzkopf commented 2 months ago

We are not actually seeing the integration start up so I think we need to take a more decisive approach to gathering the debug log. I suggest these steps:

  1. Disable the Integration ( Settings -> Integrations -> naim Mu-so -> three dots -> Disable )
  2. Restart Home Assistant ( Developer Tools -> Restart ( select full restart if asked ))
  3. Enable debug log for Integration ( Settings -> Integrations -> X disabled Show -> naim Mu-so gearwheel -> enable debug logging )
  4. Enable the Integration ( Settings -> Integrations -> naim Mu-so -> three dots -> Enable )
  5. If possible Turn on the device in HA ( Settings -> Integrations -> naim Mu-so -> 1 entity -> Vardagsrum -> wait 30 sec -> hit on off button if available )
  6. Wait 2-3 minutes
  7. Disable the Integration ( see step 1 )
  8. Disable Debug log and download log ( Settings -> Integrations -> X disabled Show -> naim Mu-so gearwheel -> disable debug logging -> accept file to download )

I have to admit that I don't fully understand the Home Assistant side of the code and how I can make it retry connecting when the initial connection fails. It's just a mess of bits code hijacked from other integration with similar functionality.

Lupi00 commented 2 months ago

Thank you for your commitment in all this.

I dont have an option to enable logging in the third step.

Instead I downloaded this log file when I enabled the integration. home-assistant_2024-08-22T06-57-56.619Z.log

I get the same errors as I have gotten all along. I alsot get error in other integrations, for example Tapo camera controll.

Makes me think there is something wrong with the installation of the integration, Maybe next step is to re-install it? (Edit: Tried reinstalling but no difference) My HA installation is 2024.4.0. Do you think that matters?

blitzkopf commented 2 months ago

In step 3 do not have this gearwheel on the naim Mu-so integration? image Behind that I can find enable debugging log.

It is a bit strange that it looks like we are trying to communicate with the device without first opening the connection. I see you are using HACS can you open HACS to make sure you using the latest version of naim mu-so integration and there is not an update available?

Lupi00 commented 2 months ago

I didnt use HACS to install the integration. I did the manual installation.

This is what I see. image

Edit: Testing installation through HACS

blitzkopf commented 2 months ago

Before pressing AKTIVERA you can do "Kända problem" that should start the debug log before you activate the integration.

And of course you did the manual installation, I have not yet published the documentation on how to use HACS for installation.

If you want to install using HACS you can do it like this.

  1. Add https://github.com/blitzkopf/naim_muso to HACS under: HACS → Integrations → 3 dots(top right) → Custom repositories.
  2. Select Naim Mu-so under HACS → Integrations, or search for it if it not on the front page.

To remove the manual installation first remove the with "Ta bort" and then do the reverse of the installation process. Go into custom_components folder and do rm -rf naim_muso

Lupi00 commented 2 months ago

Installed via HACS as you described with logging active. This is the log. home-assistant_naim_muso_2024-08-22T13-21-11.975Z.log

The problem seem to be that the integration creates the mu-so2 media_player.vardagsrum_speaker_3 as an entity but there is something wrong with making the connection to the mu-so2 so its unavailible. image

Is there a way to test this connection via developer tools here in HA or some other way?

Can the problem be that I am using 2024.4.0 version of HA? Which version are you using?

blitzkopf commented 2 months ago

I'm stating to believe Mu-so gen 2 is using a different port and possibly a different protocol.

What w can do is try to connect directly to mu-so ip-address on port 15555 like this telnet ip-address 15555 And then if you can connect send a command like this:

<command>
    <name>SetHeartbeatTimeout</name>
    <id>2</id>
    <map>
        <item>
            <name>timeout</name>
            <int>10</int>
        </item>
    </map>
</command>

You should get an answer back:

<reply name="SetHeartbeatTimeout" id="2">
</reply>

I hope you are not running Windows as I believe Microsoft removed telnet.

For me this looks like this:

[yngvi@blitzlap NaimCo]$ telnet 192.168.1.145 15555 
Trying 192.168.1.145...
Connected to 192.168.1.145.
Escape character is '^]'.
<command>
    <name>SetHeartbeatTimeout</name>
    <id>2</id>
    <map>
        <item>
            <name>timeout</name>
            <int>10</int>
        </item>
    </map>
</command>
<reply name="SetHeartbeatTimeout" id="2">
</reply>

Connection closed by foreign host.

You might also want to try port 15081 as the phone app tries that when connecting.

I have collected some information on my sniffing of the phone app at https://github.com/blitzkopf/NaimCo/blob/main/api_sniffing/sniffing.rst

If you have an Android phone you might be able to sniff the traffic using PCAPdroid just tell it you want to capture all traffic by Naim app. iPhone might also have a program to sniff traffic but I kind of doubt Apple would allow it.

I'm starting to realise that the code that takes care of the connection to the device is complete crap, and needs a rework. It will just fail silently if there is any problems connecting at startup.

Lupi00 commented 2 months ago

Does this addon work instead of telnet? It gives me a linux terminal window to enter commands. image

Otherwise I will sort this out when I get home tonight.

blitzkopf commented 1 month ago

I would suggest doing this on a PC separate from the Home assistant. The Advanced SSH & Web Terminal only gives you access to the home assistant installation which does not include a telnet client.

You should be able to get some telnet client for you PC, whatever the operating system is.

Lupi00 commented 1 month ago

I can connect through 15081 but not 15555

image

My telnet skills are to weak so not sure how to send the command you mentioned. Trying to create a script file to run but no luck so far. Will keep trying the script.

blitzkopf commented 1 month ago

Ok, I guess that confirms my suspicion that Gen-2 uses a totally different protocol. Looking at this https://github.com/sicamois/homebridge-naim-audio/blob/master/src/naimAudioAccessory.ts it looks like it a simple REST-ish protocol. If that is right you are probably better off using a program called curl than telnet. In the telnet window you could try doing GET /inputs if that work you should get some kind of list of inputs. curl http://192.168.1.52:15081/inputs should give you the same.

Lupi00 commented 1 month ago

Results in this respons

{ "version": "1.4.0", "changestamp": "0", "name": "Inputs", "ussi": "inputs", "class": "object.inputs", "cpu": "898", "children": [ { "name": "Analogue", "ussi": "inputs/ana", "class": "object.input.analogue", "alias": "", "disabled": "0", "multiroomMaster": "0", "selectable": "1" }, { "name": "Digital", "ussi": "inputs/dig", "class": "object.input.digital", "alias": "", "disabled": "0", "multiroomMaster": "0", "selectable": "1" }, { "name": "HDMI", "ussi": "inputs/hdmi", "class": "object.input.hdmi", "alias": "", "disabled": "0", "multiroomMaster": "0", "selectable": "1" }, { "name": "Internet Radio", "ussi": "inputs/radio", "class": "object.input.radio.internet", "alias": "", "disabled": "0", "multiroomMaster": "1", "selectable": "1" }, { "name": "Airplay", "ussi": "inputs/airplay", "class": "object.input.airplay", "disabled": "0", "multiroomMaster": "0", "selectable": "0" }, { "name": "Chromecast built-in", "ussi": "inputs/gcast", "class": "object.input.googlecast", "multiroomMaster": "0", "selectable": "0" }, { "name": "Spotify", "ussi": "inputs/spotify", "class": "object.input.spotify", "disabled": "0", "multiroomMaster": "1", "selectable": "1" }, { "name": "TIDAL", "ussi": "inputs/tidal", "class": "object.input.tidal", "disabled": "0", "multiroomMaster": "1", "selectable": "0" }, { "name": "Qobuz", "ussi": "inputs/qobuz", "class": "object.input.qobuz", "disabled": "0", "multiroomMaster": "1", "selectable": "0" }, { "name": "Multi-room", "ussi": "inputs/multiroom", "class": "object.input.multiroom", "multiroomMaster": "0", "selectable": "0" }, { "name": "Bluetooth", "ussi": "inputs/bluetooth", "class": "object.input.bluetooth", "alias": "", "disabled": "0", "multiroomMaster": "0", "selectable": "0" }, { "name": "Servers", "ussi": "inputs/upnp", "class": "object.input.upnp", "alias": "", "disabled": "0", "multiroomMaster": "1", "selectable": "0" }, { "name": "Playqueue", "ussi": "inputs/playqueue", "class": "object.input.playqueue", "multiroomMaster": "1", "selectable": "1" }, { "name": "USB", "ussi": "inputs/usb", "class": "object.input.usb", "alias": "", "disabled": "0", "multiroomMaster": "1", "selectable": "0" }, { "name": "Demo Files", "ussi": "inputs/files", "class": "object.input.files", "multiroomMaster": "1", "selectable": "0" } ] }

blitzkopf commented 1 month ago

Ok, thats something. Maybe you have figured this already but you should then be able to select digital input with: curl -X PUT 'http://192.168.1.52:15081/inputs/dig?cmd=select' This might turn the device on: curl -X PUT 'http://192.168.1.52:15081/power?system=on' Off (I have no idea what lona stand for): curl -X PUT 'http://192.168.1.52:15081/power?system=lona' Setting volume might work with: curl -X PUT 'http://192.168.1.52:15081/levels/room?volume=20' The volume code is commented out in the homebridge code so possibly it does not work. "room" might also somehow relate to the name of the speaker.

If these curl statement works they could then be used in a home assistant script with RESTful command integration.

Lupi00 commented 1 month ago

Now we're getting somewhere! I put in the code below to the configuration.yaml and behold the on and off works when I send this in an automation. On for on and off for.. well off.

Command to turn muso on and off

service: rest_command.naim_on
data: {}

This is amazingly awesome and exactly what I came here for :D

Now I'm just greedy and want to change input between analogue (for vinyls and cd) and digital. But cant figure out how to do it.

This is the code I added to configuration.yaml

rest_command:
  naim_on:
    url: "http://192.168.1.52:15081/power?system=on"
    method: put
    content_type: "application/x-www-form-urlencoded"
    payload: "mode=on"
  naim_off:
    url: "http://192.168.1.52:15081/power?system=lona"
    method: put
    content_type: "application/x-www-form-urlencoded"
    payload: "mode=off"
  naim_dig:
    url: "http://192.168.1.52:15081/inputs/dig?cmd=select"
    method: put
    content_type: "application/x-www-form-urlencoded"
    payload: "mode=digital"
  naim_ana:
    url: "http://192.168.1.52:15081/inputs/ana?cmd=select"
    method: put
    content_type: "application/x-www-form-urlencoded"
    payload: "mode=analogue"

Any Idea what to enter as payload and in what format?

Oh and still no entity that works via the integration, but that isn't really a big deal since I get what I want via the command.

Lupi00 commented 1 month ago

rest_command.naim_ana gives this response but doesnt change input on my Muso

content:
  version: 1.4.0
  changestamp: "0"
  name: ""
  ussi: inputs/analogue
  class: object
  cpu: "80"
status: 200
blitzkopf commented 1 month ago

I don't think you need to put anything as payload if you are using the ? parameter style, You might even just be confusing your device mixing both. I guess you could remove every thing after after and including '?' in url and place them in payload instead but the homebridge integration I referred to earlier only uses '?' style parameters so I would just stick with that. And I guess you should only set the content_type if you are actually sending some payload, so just skip that to. Maybe this will work

rest_command:
  naim_on:
    url: "http://192.168.1.52:15081/power?system=on"
    method: put
  naim_off:
    url: "http://192.168.1.52:15081/power?system=lona"
    method: put
  naim_dig:
    url: "http://192.168.1.52:15081/inputs/dig?cmd=select"
    method: put
  naim_ana:
    url: "http://192.168.1.52:15081/inputs/ana?cmd=select"
    method: put
Lupi00 commented 1 month ago

Still no luck with the dig & ana part

It works just fine when I enter the url directly into a browser but when I use the RESTful command it doesnt work.

Im not really that good with all this but I've played around with this but cant get it to work. I cant turn the muso on and off & set volume via RESTful but to set the input seems tricky. Must be something different on these two which requires the payload option? What do you think I need to try next?

blitzkopf commented 1 month ago

Do I understand you correctly that http://192.168.1.52:15081/inputs/dig?cmd=select work in a regular web browser? If so you should try to change method from put to get as that is what a browser does unless specifically told to do otherwise.

Lupi00 commented 1 month ago

Yup that did it... Halleluja :)

So clarify and summorize for anyone else who is struggling with this.

  1. I put this code in my configuration.yaml.
    rest_command:
    naim_on:
    url: "http://192.168.1.52:15081/power?system=on"
    method: put
    naim_off:
    url: "http://192.168.1.52:15081/power?system=lona"
    method: put
    naim_dig:
    url: "http://192.168.1.52:15081/inputs/dig?cmd=select"
    method: get
    naim_ana:
    url: "http://192.168.1.52:15081/inputs/ana?cmd=select"
    method: get
    naim_vol:
    url: "http://192.168.1.52:15081/levels/room?volume=30"
    method: put
  2. I use this yaml to call the service and trigger the action via RESTful command.
    service: rest_command.naim_on
    data: {}

    I can't thank you enough man.. Thank you for your patience and help!