Htd gw-sl1 / mc-66

I’m currently using a HTD (Home Theater Direct) GW-SL1 to connect to my HTD MC-66 6 zone controller. Anyone successfully connected this to Home Assistant? I’ve searched a ton, but unable to find a good how-to guide for this. I’ve tried the RUSSOUND_RNET integration, but it’s just different enough to not work unfortunately. Thanks in advance!

Hi Mark,

You had any luck? In the same boat.

Nope, sadly still in the same spot with it - just waiting for a smarter party than I to come along (or for HTD to welcome themselves to the 21st century and figure something out already…)

Will certainly post back here if I hear anything!

Sure Mark,

Yep it’s only taken what 2 years+ to get an app finally working reliably!

Been a few people asking the same question. I’ve reached out to HTD and asked the question about API integration. Collectively if we get the APIs I’m sure we can build the intergration.

Rob.

Wow! Super quick to respond…they’ve progressed somewhat.

https://www.htd.com/home-automation
Let me read over the next couple of days…maybe we could roundup some other HTD ownersvand create a component for HA??

I’ve been monitoring that page for the better part of two years, and honestly, I have seen ZERO movement. I’ve spoken to a couple different guys over at HTD (and actually picked up both of my systems directly from them [they’re only about 10 miles from my office here outside of Dallas]). Anyhow, they’re all super nice and try to be helpful - but they’ve literally, at least from my viewpoint, gotten no where - slowly.

They keep claiming that SmartThings, Google, etc. have no open APIs to use to leverage - and I’m not interested in adding yet another hub (HomeSeer, Control4, etc.) to my network/setup to try to integrate.

I’m also guessing the HomeAssistant demand to write some kind of python script simply isn’t robust enough to warrant them writing - and i’m neither knowledgeable enough / don’t have the time to fiddle with creating it myself.

They did send a bunch of docs with info on how it might be / could be / should be able to be done:



I have seen a couple of project on GitHub and around, but a lot of these focus on using a USB->Serial cable to push the commands. My setup (having two of these), needs to involve using the web connection / IP address vs. Pi -> USB-Serial Cable -> MC66.

I also ‘tried’ the baked-in Russound / HA integration (believe the control units are about the same) - and while it recognized them (somewhat), I wasn’t able fully to get it to work. Maybe the above docs coupled w/ the already supposedly working Russound integration would be a solid starting place?

With all that said - unsure anyone smarter / more willing / less time-contrained than I can take it on - and if so - I’d be honestly happy to buy a case of beer or bottle of bourbon of whatever for the efforts!

I’ve created the integration (for my needs) and posted it here: http://www.brandonclaps.com/?p=173

Running python3 /config/htdquery.py querypwr 1 0 and getting this error:

Traceback (most recent call last):
  File "/config/htdquery.py", line 15, in <module>
    detail = htd.queryZone_returndetail(int(argv[2]))
  File "/config/mca66.py", line 199, in queryZone_returndetail
    self.send_command_returndetail(cmd, zone)
  File "/config/mca66.py", line 237, in send_command_returndetail
    self.parse_reply_returndetail(data, zone)
  File "/config/mca66.py", line 113, in parse_reply_returndetail
    self.zonelist[zone]['power'] = "on" if (i[4] & 1<<7)>>7 else "off"
KeyError: 0

Tracking down the error I’m receiving into the parse_reply_returndetail function.

It’s failing on the ‘power’ line:

			for i in Zone_List:
				zone = i[2]
				self.zonelist[zone]['power'] = "on" if (i[4] & 1<<7)>>7 else "off"
				self.zonelist[zone]['input'] = i[8]+1
				self.zonelist[zone]['input_name'] = self.input_names[i[8]]
				self.zonelist[zone]['vol'] = i[9]-196 if i[9] else 0
				self.zonelist[zone]['mute'] = "on" if (i[4] & 1<<6)>>6 else "off"

I’m by far a python expert here… The ‘message being passed’ is:

b'\x01\xd8\x00\x00\x00\xe6\x02\x00\x00\x06\x00??\x00\x00\x00\x00\x00\x00\x86\x02\x00\x01\x05\x00\x00\x00\x00\x01\xe3\x00\x04\x00\xf0\x02\x00\x02\x05\x00\x00\x00\x00\x01\xec\x00\x00\x00\xf6\x02\x00\x03\x05\x00\x00\x00\x00\x01\xec\x00\x00\x00\xf7\x02\x00\x04\x05\x00\x00\x00\x00\x01\xe0\x00\x00\x00\xec\x02\x00'

Unsure what i in Zone_List or i[2] are trying to do… Let me know if there’s anything more specific I can provide. Thank you!

@Markus99 Were you ever able to get this going? I am close but still getting an “Index out of range” error running htdquery.py. It’s giving me the status but dumping a lot of errors in the logs.

$ python3 /config/htdquery.py querypwr 2 0
Zone:  2
Power:  off
Input:  1
Input Name:  Stereo
Volume:  46
Mute:  off
Traceback (most recent call last):
  File "/config/htdquery.py", line 8, in <module>
    detail = htd.queryZone_returndetail(int(argv[2]))
  File "/config/mca66.py", line 198, in queryZone_returndetail
    self.send_command_returndetail(cmd, zone)
  File "/config/mca66.py", line 236, in send_command_returndetail
    self.parse_reply_returndetail(data, zone)
  File "/config/mca66.py", line 112, in parse_reply_returndetail
    zone = i[2]
IndexError: index out of range

I honestly haven’t played with it any further. Seems like, last I did mess with it, I could pass commands but didn’t have the ability to succesfully query the zones.

My next thought’s just to leverage this to create buttons / scripts to turn on the zones and set the sources and volume at MCA startup and then just use their app to control it once it’s playing.

Also still holding out slim hope that someone smarter than I can come along and actually get this whole working.

@Markus99 I’ve made significant progress on getting this to work correctly. I have the ability to send power and volume to the htd and have the status report back to HA correctly. It is also reporting back to HA when changes are made in the app. I’ll hopefully have the mute toggle and input select going by tomorrow. I’ve also consolidate it down to 3 files without the need for any txt files to read from. Once I have it finished up I’ll post a link to the files.

Brilliant, sounds awesome @jagee23!

I wish I had the time (and python knowledge) to be of help. Excited to see some progress - I’ve been almost yelling at the HTD guys for YEARS to have SOME sort of integration (SmartThings, Google Home, HA, etc.) beyond just their app. They make pretty good hardware, but the software side is a little meh. Possibly until now!

Thrilled you’re making headway and pls do keep me updated!

@Markus99, I’ve update the code and added it to my github. https://github.com/jagee23/mca66

Take a look and see if it works for you.

The only outstanding issue I have is reporting mute state back to HA. If you toggle the mute status in the HTD app it is not reporting back to HA. However, if the change is made in HA it works correctly.

Also, there are some errors reporting in the log at times when some of the scripts run. I suspect there are multiple entities hitting the HTD gateway at the same time and it is not handling it well. I’ll continue to work on this but I dont think it is causing any issues that I can tell. I’ve actually excluded those from the logs but it would be nice if they didnt happen at all.

Wow, nice work @jagee23! I’ll download and take a look, so nice with spring / summer around the corner to be able to trigger / control these via HA! Will let you know if I see anything else / have any tidbits I can contribute. Might be a bit, as still underwater w/ family / work - but GREATLY appreciate all of your hard work / efforts!

Got a quick minute to get this setup, but haven’t started setting it up yet. Any chance you’re able to share any of the YAML you’re using on the front-end to leverage all of this great work? Thx in advance!

Right now I am just using a simple entity card and only have the zone power and volume for now. My next goal is to get everything added and have it look really nice. I am not exactly sure how I want to go with it just yet.

entities:
  - entity: switch.htd_pwr_zone1
  - entity: input_number.htd_vol_zone1
  - entity: switch.htd_pwr_zone2
  - entity: input_number.htd_vol_zone2
  - entity: switch.htd_pwr_zone3
  - entity: input_number.htd_vol_zone3
  - entity: switch.htd_pwr_zone4
  - entity: input_number.htd_vol_zone4
  - entity: switch.htd_pwr_zone5
  - entity: input_number.htd_vol_zone5
type: entities

Makes sense. I’ll see if I can find some time to mess w/ it and ‘pretty it up’ - will post back if / when - work’s killing me lately!

EDIT: OK, spent 3 minutes, will try to spare more later… :joy:

      - type: vertical-stack
        cards:
          - type: entities
            entities:
            - entity: switch.htd_pwr_zone1
              state_color: true
            - entity: input_select.htd_source_zone1
            - entity: input_number.htd_vol_zone1
          - type: entities
            entities:            
            - entity: switch.htd_pwr_zone2
              state_color: true
            - entity: input_select.htd_source_zone2
            - entity: input_number.htd_vol_zone2
          - type: entities
            entities:            
            - entity: switch.htd_pwr_zone3
              state_color: true
            - entity: input_select.htd_source_zone3
            - entity: input_number.htd_vol_zone3
          - type: entities
            entities:            
            - entity: switch.htd_pwr_zone4
              state_color: true
            - entity: input_select.htd_source_zone4
            - entity: input_number.htd_vol_zone4
          - type: entities
            entities:            
            - entity: switch.htd_pwr_zone5
              state_color: true
            - entity: input_select.htd_source_zone5
            - entity: input_number.htd_vol_zone5
          - type: entities
            entities:            
            - entity: switch.htd_pwr_zone6
              state_color: true
            - entity: input_select.htd_source_zone6
            - entity: input_number.htd_vol_zone6

Found a little more time to spend, this uses a couple of custom-cards

      ## START HTD DOWNSTAIRS STACK ##
      - type: vertical-stack
        cards:

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
              - entity: switch.speakers_downstairs
                state: "off"
              card:
                type: entities
                entities:
                - entity: switch.speakers_downstairs
                  state_color: true
                  icon: mdi:speaker
            - type: conditional
              conditions:
              - entity: switch.speakers_downstairs
                state: "on"
              card:
                type: glance
                title: Downstairs Speakers
                columns: 6
                show_name: false
                show_state: false
                entities:
                  - entity: switch.htd_pwr_zone1
                    icon: mdi:sofa
                    tap_action: 
                      action: toggle
                  - entity: switch.htd_pwr_zone2
                    icon: mdi:fire
                    tap_action: 
                      action: toggle
                  - entity: switch.htd_pwr_zone3
                    icon: mdi:waves
                    tap_action: 
                      action: toggle
                  - entity: switch.htd_pwr_zone4
                    icon: mdi:fridge-outline
                    tap_action: 
                      action: toggle
                  - entity: switch.htd_pwr_zone5
                    icon: mdi:table-chair
                    tap_action: 
                      action: toggle
                  - entity: switch.htd_pwr_zone6
                    icon: mdi:bed-empty
                    tap_action: 
                      action: toggle

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone1
                  state: "on"
              card:
                type: entities
                entities:
                  - entity: input_select.htd_source_zone1
                    icon: mdi:sofa
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone1
                  state: "on"
              card:
                type: custom:slider-entity-row
                entity: input_number.htd_vol_zone1
                full_row: true

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone2
                  state: "on"
              card:
                type: entities
                entities:
                  - entity: input_select.htd_source_zone2
                    icon: mdi:sofa
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone2
                  state: "on"
              card:
                type: custom:slider-entity-row
                entity: input_number.htd_vol_zone2
                full_row: true

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone3
                  state: "on"
              card:
                type: entities
                entities:
                  - entity: input_select.htd_source_zone3
                    icon: mdi:sofa
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone3
                  state: "on"
              card:
                type: custom:slider-entity-row
                entity: input_number.htd_vol_zone3
                full_row: true

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone4
                  state: "on"
              card:
                type: entities
                entities:
                  - entity: input_select.htd_source_zone4
                    icon: mdi:sofa
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone4
                  state: "on"
              card:
                type: custom:slider-entity-row
                entity: input_number.htd_vol_zone4
                full_row: true

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone5
                  state: "on"
              card:
                type: entities
                entities:
                  - entity: input_select.htd_source_zone5
                    icon: mdi:sofa
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone5
                  state: "on"
              card:
                type: custom:slider-entity-row
                entity: input_number.htd_vol_zone5
                full_row: true

          - type: custom:vertical-stack-in-card
            cards:
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone6
                  state: "on"
              card:
                type: entities
                entities:
                  - entity: input_select.htd_source_zone6
                    icon: mdi:sofa
            - type: conditional
              conditions:
                - entity: switch.htd_pwr_zone6
                  state: "on"
              card:
                type: custom:slider-entity-row
                entity: input_number.htd_vol_zone6
                full_row: true                                                                    
### END DOWNSTAIRS SPEAKERS VERTICAL STACK ##

Essentially it hides everything unless the AMP is on (I have my amp plugged into a smart plug (switch.speakers_downstairs).

image

Once that is on, it then unhides a glance card w/ all 6 zones.

image

Then, for each Zone that’s turned on, it unhides that zones input select and volume slider.

Personally, I leave the amp OFF unless I’m using it, so also trying to adjust the script to not check the switches/sensors unless that switch.speakers_downstairs is on. Haven’t checked to confirm all of the .py is working and such, but figured I’d post what I’d done to-date. Thinking about adding a row of buttons w/in each zones conditional card as ‘quick action / presets’ that trigger scripts to set that zone to a certain input and/or volume - or volume up 3 / down 3 buttons. Also might do a row at the top of quick presets - all outside on/off, all inside on/off, all to source X, etc…

I like it…nice job! Keep me updated as you make progress. I was also looking at the mini media player card - https://github.com/kalkih/mini-media-player and thinking about how I could potentially incorporate that. I primarily use the 1st source input so I think this could work.

Also, I’ve been thinking about the mute function issue. I’m thinking about just abandoning that all together. Wouldn’t turning the zone off and back on serve the same purpose as toggling the mute or can you think of a reason the toggle would be better?

Just getting back to this, again, and seeing that about 1/2 the time I run a command (this was run on the console via Docker), I’m seeing something like this:

bash-5.0# python3 /config/packages/mca66/htd.py pwr 1 1
ParseReply_MessagePassedIn:  b'\x00\x00\x00\x01\xdf\x00\x00\x00\xec\x02\x00\x06\x05\x00\x00\x00\x00\x01\xe2\x00\x00\x00\xf0\x02\x00\x01\x05\x80\x07\xc2\x01\x01\xde\x00\x04\x005'
{
    "error": 1
}

If I re-run the command, it’ll typically return the correct values.

bash-5.0# python3 /config/packages/mca66/htd.py pwr 1 1
ParseReply_MessagePassedIn:  b'\x02\x00\x01\x05\x80\x07\xc2\x00\x01\xde\x00\x04\x004\x02\x00\x01\x05\x80\x07\xc2\x01\x01\xde\x00\x04\x005'
{
    "Zone": 1,
    "Power": "on",
    "Input": 2,
    "Volume": 26,
    "Mute": "off"
}

Also, seeing this when trying to getzone

bash-5.0# python3 /config/packages/mca66/htd.py getzone 1
ParseReply_MessagePassedIn:  b'\x00\xf0\x02\x00\x00\x06\x00??\x00\x00\x00\x00\x00\x00\x86\x02\x00\x01\x05\x80\x07\xc2\x00\x01\xde\x00\x04\x004\x02\x00\x02\x05\x00\x00\x00\x00\x01\xde\x00\x00\x00\xe8\x02\x00\x03\x05\x00\x00\x00\x00\x01\xe0\x00\x00\x00\xeb\x02\x00\x04\x05\x00\x00\x00\x00\x01\xda\x00\x00\x00\xe6\x02\x00\x05\x05\x00\x00\x00\x00\x01\xdf\x00\x00\x00\xec'
{
    "error": 1
}

Unsure if it’s just my gateway that hates me (it’s an older one [had it about 5-6 years now]) or what…