Xantech / Dayton Audio / Sonance multi-zone amps

@skavan Thanks for posting your config, will be useful for me to read through to understand your mental model for interacting with a device. This is similar to what I’ve been working though (modeling the interfaces, what models it applied to, etc). What I’ve run across is that so much is shared, but slightly different across models. I’m looking at Apple PKL (relatively new and may not be a good option since limited support today), as well as Nickel, Nix, RCL, etc.

The biggest need for me is the ability to import portions of the configuration, ability to delete/override some configuration, easy to read, and as cross-language as possible (so other people can create similar tools in other languages using the existing configuration). Also would be cool to be able to generate YAML/JSON from one of the configurations, just for wider compatibility with other tools.

I should have something very interesting for you in a few weeks for IP control if the API over TCP is text based.

Cool. We should coordinate!
Here’s the beginnings of my version 2, supporting variables, regex and math expressions.
It also supports overrides. We should probably put into a repository instead of clogging up this channel.

  1. Predefined Capabilities (very basic so far):
[
    {
        "capability": "switch",
        "name": "power",
        "description": "Power",
        "actions": {
            "onoff": {
                "command": "",
                "arguments": [
                    {
                        "name": "setPower",
                        "type": "enum",
                        "enum": ["on", "off"]
                    }
                ],
                "valueMap": [
                    { "value": "on", "deviceValue": 1 },
                    { "value": "off", "deviceValue": 0 }
                ]
            },
            "status": { "command": "", "arguments": [] },
            "toggle": { "command": "", "arguments": [] }
        },
        "allowedValues": ["on", "off"],
        "displayFormat": ""
    },
    {
        "capability": "audioVolume",
        "name": "volume",
        "description": "Volume Control",
        "actions": {
            "volume": {
                "command": "",
                "arguments": [
                    {
                        "name": "setVolume",
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                    }
                ]
            },
            "volumeUp": { "command": "", "arguments": [] },
            "volumeDown": { "command": "", "arguments": [] },
            "status": { "command": "", "arguments": [] }
        }
    },
    {
        "capability": "mediaInput",
        "name": "input",
        "description": "Media Source Control",
        "actions": {
            "input": {
                "command": "",
                "arguments": [
                    {
                        "name": "setInput",
                        "type": "enum",
                        "enum": ["Input 1", "Input 2", "Input 3", "Input 4", "Input 5", "Input 6", "Input 7", "Input 8"]
                    }
                ]
            }
        }
    },
    {
        "capability": "audioMute",
        "name": "mute",
        "description": "Audio Mute",
        "actions": {
            "onoff": {
                "command": "",
                "arguments": [
                    {
                        "name": "setMute",
                        "type": "enum",
                        "enum": ["on", "off"]
                    }
                ]
            },
            "status": { "command": "", "arguments": [] },
            "toggle": { "command": "", "arguments": [] }
        },
        "allowedValues": ["on", "off"],
        "displayFormat": ""
    }
]```

2. Beginnings of a revised Matrix controller.
```{
	"id": "i5TTEWeL",
	"name": "MultiZoneAmp",
	"description": "Xantech Matrix Switcher",
	"version": "1.0.0",
    "type": "Amplifier",
	"category": "AV",
	"need a name": "matrix switch",
	"license": "MIT",
	"author": "Suresh Kavan",
	"supportedModels": ["a", "b"],
	"protocol": {
		"interface": "tcp",
		"port": 4999,
		"delimiterOut": "",
		"delimiterIn": "+\r",
		"substitutionCharacter": "$",
		"responseParser": "regex",
		"messageDelay": 250,
		"messageTimeout": 1000
	},
    "preProcessorCommand": "{%pp}",
    "variablePlaceholder": "%",

	// first we have a device. (matrix001)
	// then we have a component (main)
	// then we have a capability (switch, audioVolume, mediaInput, audioMute)
	// capabilities have one or more properties,
	// that are defined by the capability statement
	// i.e. power, volume, volumePct, volumedB, mute, input

	// matrix001/main/switch/power
	// matrix001/main/audioVolume/volume
	// matrix001/main/audioVolume/volumedB
	// matrix001/main/audioVolume/volumePct
	// matrix001/main/audioMute/mute
	// matrix001/main/mediaInput/input

    // commands, components, feedbacks, variables can by of type "array"


	"components": [
		{
			"component": "main",
			"name": "main",
			"description": "Master Controls",
			// pull in the capabilty and override here
			"capabilities": [
				{
					"capability": "switch",
					"name": "power",
					"description": "All Zones Power"
				},
				{
					"capability": "audioVolume",
					"description": "All Zones Volume"
				},
				{
					"capability": "mediaInput",
					"description": "All Zones Source"
				},
				{
					"capability": "audioMute",
					"description": "All Zones Mute"
				}
			],
			"actions": {
				"switch": {
					"onoff": {
						"command": ["!1PR{%1}+", "!2PR{%1}+"]
					},
					"status": {
						"command": ["?1PR+", "?2PR+"]
					},
					"toggle": {
						"command": ["!1PT+", "!2PT+"]
					}
				},
				"audioVolume": {
					"volume": {
						"command": ["!1VO${%1}+", "!2VO${%1}+"],
						"arguments": [
							{
								"name": "setVolume",
								"type": "integer",
								"minimum": 0,
								"maximum": 38
							}
						]
					},
					"volumeUp": {
						"command": ["!1VI$1+", "!2VI$1+"],
						"arguments": []
					},
					"volumeDown": {
						"command": ["!1VD$1-", "!2VD$1-"],
						"arguments": []
					}
				}
			}
		},
		{
			"component": "zone{%pp}",
            // doesn't have to be integers, could be text too.
            "type": {"array": [1, 2, 3]} ,
			"name": "Zone{%pp}",
			"description": "Zone {%pp} Controls",
			"capabilities": [
				{
					"capability": "switch",
					"name": "power",
					"description": "Zone {%pp} Power"
				},
				{
					"capability": "audioVolume",
					"description": "Zone {%pp} Volume"
				},
				{
					"capability": "mediaInput",
					"description": "Zones {%pp} Source"
				},
				{
					"capability": "audioMute",
					"description": "Zone {%1} Mute"
				}
			],
			"actions": {
				"switch": {
					"onoff": {
						"command": "!{%pp}PR{%1}+"
					},
					"status": {
						"command": "?{%pp}PR+"
					},
					"toggle": {
						"command": "!{%pp}PT+"
					}
				},
				"audioVolume": {
					"volume": {
						"command": "!{%pp}VO{%1}+",
						"arguments": [
							{
								"name": "setVolume",
								"type": "integer",
								"minimum": 0,
								"maximum": 38
							}
						]
					},
					"volumeUp": {
						"command": "!{%pp}VI+",
						"arguments": []
					},
					"volumeDown": {
						"command": "!{%pp}VD+",
						"arguments": []
					}
				}
			}
		}
	],
    "feedbacks": [
        {"name": "Power", "match": "\\?(\\d)PO(\\d)\\+", "component": "zone{%1}", "capability": "switch", "value": "{%2}", "variables": ["zonePower"]},
		{"name": "Volume", "match": "\\?(\\d)VO(\\d{1,3})\\+", "component": "zone{%1}", "capability": "audioVolume", "value": "{%2}", "variables": ["zoneVolume"]}
    ],
    "variables":[
        {"name":"masterPower", "formula": "${zone1.switch}} + ${zone2.switch}} + ${zone3.switch} > 0", "type":"boolean"},
    ],

    // these override predefined capabilities
	"capabilities": [
        {
            "capability": "mediaInput",
            "name": "input",
            "description": "Media Source Control",
            "actions": {
                "input": {
                    "command": "",
                    "arguments": [
                        {
                            "name": "setInput",
                            "type": "enum",
                            "enum": ["Sonos", "Input 2", "Input 3", "Input 4", "Input 5", "Input 6", "Input 7", "Input 8"]
                        }
                    ]
                }
            }
        }
    ]
}

@ryans … please rememebr I also posted enhanced (hacked as best I could) code for bass/treble/balance. While I know that many set it and forget it, sometimes this can be valuable. My GUI now has this for every zone in my DAX:

Is there any chance you would consider cooperation to also bring the bass, treble and balance settings for the Soundavo ws66i integration? (now part of home assistant)
Browsing through the code, I see lines describing the setting of these, but I don’t see them in the actual entities/devices in home assistant

I am only a “hack” when it comes to python coding. I am hoping that @ryans will have time to review and eventually put this into the code for all the players (and done the way it should be).

As such, I chose not to create things beyond attributes off the media_player entity.

Then I implemented services (xantech.set_bass, xantech.set_treble, xantech.set_balance so that they can be changed in say an automation.

I just tied a input_number to these through an automation. The only thing I would guess is missing would be to have created those input_number entities under each zone, but I though that overkill (3 x 8 zones = 24 input_number entities … versus 3 attributes on each zone).

alias: Change DAX Bass_Treble_Balance
description: ""
trigger:
  - platform: state
    entity_id:
      - input_number.dax_bass_level
      - input_number.dax_treble_level
      - input_number.dax_balance_level
  - platform: state
    entity_id:
      - input_select.dax_zones
condition: []
action:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.entity_id == 'input_number.dax_treble_level' }}"
        sequence:
          - service: xantech.set_treble
            target:
              entity_id: >
                {% set selzone = states('input_select.dax_zones') %} {% for
                entity_id in state_attr('group.dax_media_players','entity_id')
                %} {% if state_attr(entity_id,'friendly_name') == selzone %} {{
                entity_id }} {% endif %} {% endfor %}
            data_template:
              treble: "{{ states(trigger.entity_id) | int }}"
      - conditions:
          - condition: template
            value_template: "{{ trigger.entity_id == 'input_number.dax_bass_level' }}"
        sequence:
          - service: xantech.set_bass
            target:
              entity_id: >
                {% set selzone = states('input_select.dax_zones') %} {% for
                entity_id in state_attr('group.dax_media_players','entity_id')
                %} {% if state_attr(entity_id,'friendly_name') == selzone %} {{
                entity_id }} {% endif %} {% endfor %}
            data_template:
              bass: "{{ states(trigger.entity_id) | int }}"
      - conditions:
          - condition: template
            value_template: "{{ trigger.entity_id == 'input_number.dax_balance_level' }}"
        sequence:
          - service: xantech.set_balance
            target:
              entity_id: >
                {% set selzone = states('input_select.dax_zones') %} {% for
                entity_id in state_attr('group.dax_media_players','entity_id')
                %} {% if state_attr(entity_id,'friendly_name') == selzone %} {{
                entity_id }} {% endif %} {% endfor %}
            data_template:
              balance: "{{ states(trigger.entity_id) | int }}"
      - conditions:
          - condition: template
            value_template: "{{ trigger.entity_id == 'input_select.dax_zones' }}"
        sequence:
          - service: input_number.set_value
            data_template:
              value: >
                {% set selzone = states('input_select.dax_zones') %} {% for
                entity_id in state_attr('group.dax_media_players','entity_id')
                %} {% if state_attr(entity_id,'friendly_name') == selzone %}
                {{state_attr(entity_id,'balance') }} {% endif %} {% endfor %}
            target:
              entity_id: input_number.dax_balance_level
          - service: input_number.set_value
            data_template:
              value: >
                {% set selzone = states('input_select.dax_zones') %} {% for
                entity_id in state_attr('group.dax_media_players','entity_id')
                %} {% if state_attr(entity_id,'friendly_name') == selzone %}
                {{state_attr(entity_id,'bass') }} {% endif %} {% endfor %}
            target:
              entity_id: input_number.dax_bass_level
          - service: input_number.set_value
            data_template:
              value: >
                {% set selzone = states('input_select.dax_zones') %} {% for
                entity_id in state_attr('group.dax_media_players','entity_id')
                %} {% if state_attr(entity_id,'friendly_name') == selzone %}
                {{state_attr(entity_id,'treble') }} {% endif %} {% endfor %}
            target:
              entity_id: input_number.dax_treble_level
mode: single

I suppose it could have been one service instead of three and set them all, but the service takes in the target entity and the value to set it to with value checking. This is for bass. Now that I write this it could have been just set level and likely took in bass, treble and balance.

Certainly this has the service references:

And the attributes as extras:

    @property
    def extra_state_attributes(self):
        """Return the extra state attributes for bass, treble, balance."""
        bass = self._status.get('bass')
        treble = self._status.get('treble')
        balance = self._status.get('balance')
        return {
            "bass": bass,
            "treble": treble,
            "balance": balance
        }

Carried in constants:

Of course, I made mods in the series and protocols of pyxantech:

The key to another integration would be to modify this I believe for your particular receiver. :

    set_volume:    '<{zone}VO{volume:02}'   # volume: 0-38
    set_treble:    '<{zone}TR{treble}:02}'  # treble: 0-14
    set_bass:      '<{zone}BS{bass:02}'     # bass: 0-14
    set_balance:   '<{zone}BL{balance:02}'  # balance: 0-20
    set_source:    '<{zone}CH{source:02}'   # source: 0-6

And also the overall get current state might need to be modified to be sure it returns bass, treble and balance and restore sets them.

  responses:
    zone_status: '>(?P<zone>\d\d)(?P<unknown>\d\d)(?P<power>[01]{2})(?P<mute>[01]{2})(?P<do_not_disturb>[01]{2})(?P<volume>\d\d)(?P<treble>\d\d)(?P<bass>\d\d)(?P<balance>\d\d)(?P<source>\d\d)(?P<keypad>\d\d)'

  extra:
    restore_zone:    [ 'set_power', 'set_source', 'set_volume', 'set_mute', 'set_bass', 'set_balance', 'set_treble' ]
    restore_success: "OK\r"

Tying it together, it rocks:

2 Likes

In regards to balance/bass/treble control questions/suggestions from@AlexMPH and @kbrown01, I created a new thread on adding this support to Home Assistant to allow ALL media player entities to provide support for these interfaces.

This is the best layer to add this so that it becomes codified as a standard that any media player integration can support.

Here is the thread, please feel free to start contributing there. I don’t have the time to setup an environment and test the changes to Home Assistant core right now or I would.

Hopefully some enthuasiastic developer takes this up as a fun project to add to core…it even would make a great first project.

See thread here:

YOU CAN ALSO VOTE FOR THIS FEATURE on the above link.

1 Like

Voted and thanks!

I added a comment that specifically this should be targeted at stereo controls and not surround sound systems.

1 Like

I’m new to HA and was hoping for some help getting this set up to control my Sonance C4630 SE in HA.
If I used this in the config

media_player:
  - platform: xantech
    type: sonance

I get an error saying
Invalid config for ‘xantech’ from integration ‘media_player’ at configuration.yaml, line 14: value must be one of [‘dax88’, ‘monoprice6’, ‘sonance6’, ‘xantech8’, ‘zpr68-10’] for dictionary value ‘type’, got ‘sonance’

If I change it to sonance6 I get this error.
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/entity_platform.py”, line 350, in _async_setup_platform
await asyncio.shield(awaitable)
File “/config/custom_components/xantech/media_player.py”, line 137, in async_setup_platform
amp = await async_get_amp_controller(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/pyxantech/init.py”, line 568, in async_get_amp_controller
protocol_config = PROTOCOL_CONFIG[protocol]
~~~~~~~~~~~~~~~^^^^^^^^^^
KeyError: ‘sonance6’

Any help would be appreciated… Joe

Did you make any headway with this ? I seem to be in the same boat.

I can now control the Xantech MRC88 with an Itach IP2SL from my Mac and via USB to Serial cable from a windows VM but I cannot seem to control via home assistant either via IP or USB. The media player entities show up but I’m unable turn them on and just get the error -

‘Failed to call service media_player/turn_on. unknown error’

Anyone have any ideas ? I’m running HA as a VM on an Unraid server if that makes any difference.

Edit: I can control the zones (on and off) via telnet switch through HA.

Also to add, this is the error showing in HA logs for all zones -

WARNING (MainThread) [custom_components.xantech.media_player] Failed updating Xantech House Audio zone 13 (Living Room):

Ok, I’ve managed to get it working and my issue appears to have been using 11,12,13 etc for my zones rather than 1, 2, 3…

Not sure why as I originally copied the example config from github and changed it but it seems to be working as expected now. Hopefully this helps anyone else who might come across the same issue in future.

Also, this is working over an iTach IP2SL.

hi all

so for the DAX88 - if you have say 6 pairs of speakers hard wired to it - you can cast youtube music (like you cast from the app on your phone) to any / all zones?

This seems like exactly what i’m looking for.

thank you

anybody else have this problem after upgrading to HA 2024.6 ?

Logger: homeassistant.config
Source: config.py:1440
First occurred: 11 June 2024 at 22:40:04 (1 occurrences)
Last logged: 11 June 2024 at 22:40:04

Platform error: media_player - cannot import name 'SerialException' from 'serial' (/usr/local/lib/python3.12/site-packages/serial/__init__.py)
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config.py", line 1440, in _async_load_and_validate_platform_integration
    platform = await p_integration.integration.async_get_platform(domain)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1085, in async_get_platform
    platforms = await self.async_get_platforms((platform_name,))
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1162, in async_get_platforms
    import_future.result()
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1150, in async_get_platforms
    platforms.update(self._load_platforms(platform_names))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1075, in _load_platforms
    platform_name: self._load_platform(platform_name)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1249, in _load_platform
    cache[full_name] = self._import_platform(platform_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1281, in _import_platform
    return importlib.import_module(f"{self.pkg_path}.{platform_name}")
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/util/loop.py", line 131, in protected_loop_func
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/config/custom_components/xantech/media_player.py", line 8, in <module>
    from serial import SerialException
ImportError: cannot import name 'SerialException' from 'serial' (/usr/local/lib/python3.12/site-packages/serial/__init__.py)

what I see that the serial package is really missing:

PyYAML-6.0.1.dist-info             packaging                          pyparsing
PyYAML.libs                        packaging-24.0.dist-info           pyparsing-3.1.2.dist-info
README.txt                         pathspec                           setuptools
_distutils_hack                    pathspec-0.12.1.dist-info          setuptools-69.5.1-py3.12.egg-info
_yaml                              pip                                yaml
awake                              pip-24.0-py3.12.egg-info           yamllint
awake-1.0-py3.12.egg-info          pkg_resources                      yamllint-1.35.1.dist-info
distutils-precedence.pth           pulsemixer-1.5.1.dist-info
➜  site-packages pip uninstall serial
WARNING: Skipping serial as it is not installed.
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
➜  site-packages pip uninstall pyserial
WARNING: Skipping pyserial as it is not installed.
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
➜  site-packages pip install pySerial                 
Looking in indexes: https://pypi.org/simple, https://wheels.home-assistant.io/musllinux-index/
Collecting pySerial
  Downloading https://wheels.home-assistant.io/musllinux-index/pyserial-3.5-py2.py3-none-any.whl.metadata (1.6 kB)
Downloading https://wheels.home-assistant.io/musllinux-index/pyserial-3.5-py2.py3-none-any.whl (90 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 90.6/90.6 kB 3.9 MB/s eta 0:00:00
Installing collected packages: pySerial
Successfully installed pySerial-3.5
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
➜  site-packages ls
PyYAML-6.0.1.dist-info             packaging-24.0.dist-info           pyserial-3.5.dist-info
PyYAML.libs                        pathspec                           serial
README.txt                         pathspec-0.12.1.dist-info          setuptools
_distutils_hack                    pip                                setuptools-69.5.1-py3.12.egg-info
_yaml                              pip-24.0-py3.12.egg-info           yaml
awake                              pkg_resources                      yamllint
awake-1.0-py3.12.egg-info          pulsemixer-1.5.1.dist-info         yamllint-1.35.1.dist-info
distutils-precedence.pth           pyparsing
packaging                          pyparsing-3.1.2.dist-info
➜  site-packages cd serial 
➜  serial ls
__init__.py     __pycache__     rs485.py        serialjava.py   serialutil.py   threaded        urlhandler
__main__.py     rfc2217.py      serialcli.py    serialposix.py  serialwin32.py  tools           win32.py

I tried reinstalling serial - it shows up but disappears after reboot…

After it is reinstalled, when I go to Developer Tools it still complains about missing “serial” and init.py

Configuration warnings
Platform error 'media_player' from integration 'xantech' - cannot import name 'SerialException' from 'serial' (/usr/local/lib/python3.12/site-packages/serial/__init__.py)

Anyone has an idea what happened?
For a while yesterday I had 2 USB SERIAL connected - one for DAYTON and one for Cambridge Audio CXA61. I have also installed CXA61 integration and forwarded another USB_to_serial (CXA61) to my VM running HA (on QNAP NAS).
After starting it the CXA61 integration did work but generated lags and only switching on was forwarded to CXA and it didn;t really react much.
After a restart or two both integrations broke and I started seeing the missing serial error.
I removed second serial (CXA61), made sure first serial is discovered as ttyUSB0 configured in xantech integration, restarted HA several times.
I even disconnected both serial and removed both integrations from config and made several restarts with no serials attached - no go.
I also rebooted QNAP host and also (software removed second serial (CXA61) passthrough and rebooted again with no serials attached,
Then rebooted with only xantech aserial attached.
no go.
no more ideas where to try…
Can anyobe help me solving this issue please?

Maybe this also has something to do with my problem?
I started seeing this in logs recently:

Logger: homeassistant.util.loop
Source: util/loop.py:84
First occurred: 04:57:11 (2 occurrences)
Last logged: 05:00:46

Detected blocking call to import_module inside the event loop by integration 'config' at homeassistant/components/config/core.py, line 38: res = await check_config.async_check_ha_config_file(request.app[KEY_HASS]) (offender: /usr/src/homeassistant/homeassistant/loader.py, line 1281: return importlib.import_module(f"{self.pkg_path}.{platform_name}")), please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+config%22 Traceback (most recent call last): File "<frozen runpy>", line 198, in _run_module_as_main File "<frozen runpy>", line 88, in _run_code File "/usr/src/homeassistant/homeassistant/__main__.py", line 223, in <module> sys.exit(main()) File "/usr/src/homeassistant/homeassistant/__main__.py", line 209, in main exit_code = runner.run(runtime_conf) File "/usr/src/homeassistant/homeassistant/runner.py", line 190, in run return loop.run_until_complete(setup_and_run_hass(runtime_config)) File "/usr/local/lib/python3.12/asyncio/base_events.py", line 672, in run_until_complete self.run_forever() File "/usr/local/lib/python3.12/asyncio/base_events.py", line 639, in run_forever self._run_once() File "/usr/local/lib/python3.12/asyncio/base_events.py", line 1988, in _run_once handle._run() File "/usr/local/lib/python3.12/asyncio/events.py", line 88, in _run self._context.run(self._callback, *self._args) File "/usr/local/lib/python3.12/site-packages/aiohttp/web_protocol.py", line 452, in _handle_request resp = await request_handler(request) File "/usr/local/lib/python3.12/site-packages/aiohttp/web_app.py", line 543, in _handle resp = await handler(request) File "/usr/local/lib/python3.12/site-packages/aiohttp/web_middlewares.py", line 114, in impl return await handler(request) File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 92, in security_filter_middleware return await handler(request) File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 83, in forwarded_middleware return await handler(request) File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 26, in request_context_middleware return await handler(request) File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 242, in auth_middleware return await handler(request) File "/usr/src/homeassistant/homeassistant/components/http/headers.py", line 32, in headers_middleware response = await handler(request) File "/usr/src/homeassistant/homeassistant/helpers/http.py", line 73, in handle result = await handler(request, **request.match_info) File "/usr/src/homeassistant/homeassistant/components/http/decorators.py", line 81, in with_admin return await func(self, request, *args, **kwargs) File "/usr/src/homeassistant/homeassistant/components/config/core.py", line 38, in post res = await check_config.async_check_ha_config_file(request.app[KEY_HASS])

Of course I introduced more problems by upgrading HA to 2024.6.2 when playing with integrations - now I have no clue what might have caused this issue…

EDIT:
I can confirm that reverting back from CORE 2024.6.2 to CORE 2024.6.1 brings back the integration and it is working fine.
So I can confirm, that at least in my situation it is latest CORE update 2024.6.2 that was to blame. Heads up.

EDIT2:
So it was not the HA CORE 2024.6.2 fault.
Looks like adding second usb-serial device to my HA has broke it.
I have just tried to readd CXA61 via second serial to the same HA host, and it started again.
Actually upgrading to 2024.6.2 resolved my issue this time (after disconnecting second serial).
Is there a way to have 2 integrations for 2 audio devices connected through serial-usb adapter?

Just a question … do you have the standard integration or mine (which includes bass/treble/balance controls). I have not upgraded yet precisely because I have a total custom where I have those controls. Just want to be sure it isn;t something I did.

Doesn’t look like it from the errors, just usb to serial error but you never know.

I have installed integrarion from HACS - the original version.
I will be upgrading to yours soon.

Mine is a total hack for the Dayton Audio device. It has been discussed and the author (rightly so) thinks bass/treble/balance should be in the core media player so that everyone can enjoy such things.

I just use personal version until that happens.

1 Like

Hi - I am trying to figure out how to configure/program the DAX88 with HA over our ethernet network, using an IP address as the “port”, but I’m not sure how to do so. I have HACS installed, and I was able to download the “Xantech/Dayton Audio/Sonance Multi-Zone Amp Control for Home Assistant” package.

I went into the “Terminal” and changed directory to the “config” folder, and then edited the configuration.yaml file to add the following code:

media_player:

platform: xantech
type: dax88
port: socket://192.168.1.242:443
zones:
11:
name: “Living Front”
12:
name: “Living Back”
13:
name: “Living Center”
14:
name: “Kitchen”
15:
name: “Master Bedroom”
16:
name: “Basement Porch”
17:
name: “Living Subwoofer”
18:
name: “ZONE8”
sources:
1:
name: “input1”
2:
name: “input2”
3:
name: “Input3”
4:
name: “Input4”
5:
name: “HDTV”
6:
name: “Sonos”
7:
name: “Input7”
8:
name: "Wi-Fi"

I can go to “Quick reload” under the “Settings” menu, and the yaml seems to load just fine (I don’t get any warnings). Then, I added the “Media Control” card to one of my Dashboards, but it only shows our Sonos (it was one of the options I had to pick under the “Entity” drop down menu when adding the card).

At this point, I’m not sure what to do to get a list of speaker sets, their volumes, and the power button to the right (I’m assuming this is the ability to wake/sleep each speaker set) like others are showing in this thread. I get the feeling that I need to change something and/or install a different media player, but I’m not sure which one, how to get it, or how to install.

Any help would be greatly appreciated.

I think I made some progress here with the programming of the configuration.yaml.

When using port “888” as suggested here: GitHub - rsnodgrass/hass-xantech: Xantech Multi-Zone Matrix Audio for Home Assistant

I get an error that the “zones” cannot load.

When checking for the communicating ports using nmap, I get the following list of “open” ports: 53, 80, 443, 8899, 49152.

When I change the “port” in the configuration.yaml file to one of those above, the system now seems to recognize each “zone”. Although, there is a “Failed updating” error during startup (and thereafter).

And if I try to click the speaker in the Entities settings to turn on one of the zones, I get another error that says “failed to call service” “unknown error”.

The error logs show the following:

At this point I’m completely stuck. Perhaps this is not compatible over our ethernet connection? Any help would be appreciated.

I have Dax88 Dayton that has 6 + 2 Zones. We are using the Ethernet port to communicate with it via its own app for now.
Does HA work with Ethernet port or we must use the Serial converter cable?
Thank you

Just got my Dax88 running using hass-xantech connected to a Rpi running ser2net. Dumping configs here in case anyone finds them useful. I got stuck for a while because I didn’t realize the Dax88 zone numbering starts at 11.

/etc/ser2net/ser2net.yaml on Raspberry Pi:

%YAML 1.1
---

connection: &dax88
    accepter: telnet,tcp,8888
    enable: on
    options:
      kickolduser: true
      telnet-brk-on-sync: true
    connector: serialdev,/dev/ttyUSB0,9600n81,local

admin: &dax88admin
  accepter: telnet,tcp,8889

Addition to HA configuration.yaml:

media_player:
  - platform: xantech
    type: dax88
    port: socket://<your-pi-ip-here>:8888/
    zones:
      11:
        name: "Side Room"
      12:
        name: "Kitchen"
      13:
        name: "Living Room"
    sources:
      1:
        name: "Mixer"
      8:
        name: "Wi-Fi"

This is also assuming a fixed IP for the Rpi… I feel like there has to be a way to reverse-engineer the Matrio app for direct network control, but rs232 is working for me so probably won’t investigate.