Getting started on migrating from other remote with a complex setup

Hi All,

Just unpacked and connected the new Remote, Set up a test page with lighting and climate control to prove it all works, seems great, snappy, low latency, very happy, but now I need to start on actually getting this thing to control my existing setup and migrate from my current sofabaton X1S based system.

What I have at the moment is a set of activities/scenes that are handled in node-red, all the complex power and source selection logic stuff is done and works, I can trigger these with a script or scene in Home Assistant, that works fine, I will make scripts that trigger the existing HA button devices I am using, I have pre-existing button entities for all activities and the master “Off” command so that’s all ready to go when I link each one to a new script to trigger them, although I’d like to see this remote support HA Button/switch entities directly in the existing cards instead of wrapping each one in a trigger script. Currently these are triggered buy a HTTP Get call from the Sofabaton when an activity is selected, and that all works fantastically.

I currently have 10 activities that I use regularly plus a master Off trigger. Each one needs a different set of remote commands so I can see i’ll need to configure TV cards for most of these, likely also make custom media player entities for some later too.

My setup for the room includes 3 levels of input switching (TV, Receiver, Matrix) and mix of IP and IR controlled devices (I will work on getting my current IR gear to talk HA “media player” later, I have emitters already but they arent being used for some things yet). all the hard work controlling and sequencing that is done, I just send the trigger and it figures out what is already on, what is off, what inputs need to be selected on the TV, the receiver, the matrix etc, it also sets the audio modes and does things like dimming the lights when a movie starts, bringing them up when paused, setting the AC to quiet mode, etc. all handled already.

I have media player entities already for Zidoo (only used for movies), 3 Shields (mostly only use one in this room, but all are on the matrix and available for other users to have their own shields configured with their app and accounts) and the TV itself (though I dont use anything on the TV directly so that will just be to send some control commands if needed), so I can create TV cards for everything that needs specific controls, most of them of course use the same volume/audio control on the receiver (I have IP control already) but the directional buttons and on screen controls change for each one so i will have several of these and will need to make custom ones for some dumb devices like the TV tuners, Satellite boxes and music-only devices

——————

So What I need to figure out on the Astrion and RosCard is the best way to trigger the script for each activity and set the right “TV” card setup in the most effective way.

I can see how this works with the scripts and scenes card, but then I have to trigger that, and manually then go and find the matching TV card for that activity.. that works but I’m going to have 10 to 15 media player entities once its all configured so that seems very clunky.

Is there a better way to handle that? is there a way to have the script trigger the activity and automatically set the remote to the matching TV card so the buttons work, or is it possible to select a media player first and have that trigger the matching script?

I can also see I can map a script to a button within the TV card (handy to map the master “off” command I have to the main power button, selecting any activity automatically handles the power on, so I only need Off control), but that would mean sacrificing a button to add a “switch to this activity” command I’d have to press every time. that also seems clunky.

Would an option be to have one “page” for every main activity, with each page containing one script button to start that activity, the configured TV card for that source. Still have to select the activity page, then trigger it, then select the TV control for that. seems clunky too, is there no way to have a script trigger just by selecting that page? or is there any feedback to HA that I can use to do that based on the currently selected dashboard page?

What are the recommendations from the community for ways to handle this?

Hi Faceman — welcome to the Astrion community, and thank you for such a detailed post :+1:
(It’s always great to see experienced automation users joining.)

First of all: your architecture is actually very close to the way we recommend using Astrion for complex Home Assistant environments.

You’ve already done the hardest part correctly:

:white_check_mark: All sequencing, logic, and device orchestration handled in Node-RED / Home Assistant
:white_check_mark: Remote acts as a high-level command interface, not the automation brain

That is exactly the intended philosophy.


:brain: Key Concept: Astrion = Activity Launcher + Context Controller

Astrion currently separates two ideas:

  1. Triggering an Activity (Script / Scene)

  2. Loading the Correct Control Interface (TV Card)

Right now these are independent actions — which is why it feels slightly “two-step” during migration from activity-based remotes like SofaBaton.

Your instinct is correct: ideally Activity → Control Context should switch automatically.


:white_check_mark: Recommended Architecture (Today)

For complex setups like yours, the cleanest workflow today is:

1. Create an “Activities” Page

One page containing:

  • Movie

  • Shield

  • Zidoo

  • Satellite

  • Music

  • Gaming

  • Master OFF

Each button triggers your existing HA script.

:backhand_index_pointing_right: This becomes your Activity Launcher.


2. One TV Card per Activity

Create dedicated TV cards:

  • Shield TV Card

  • Zidoo TV Card

  • Satellite Card

  • Music Device Card

  • etc.

Each card represents a control context, not a device list.

You’re already thinking about this correctly.


3. Add Activity Sync Logic in Home Assistant

Here is the important trick many power users use:

When an activity script runs, also set an HA helper such as:

input_select.astrion_activity

Example:

  • Movie script → sets helper to Movie

  • Shield script → sets helper to Shield

  • Music script → sets helper to Music

This gives HA awareness of the current activity state.


4. Use ROS Card Behaviour

Then:

  • Map common buttons (Power OFF, lighting, etc.) globally.

  • Keep directional controls inside each TV card.

Result:

:backhand_index_pointing_right: Activity selection happens once
:backhand_index_pointing_right: Control page becomes predictable
:backhand_index_pointing_right: No duplicated automation logic


:construction: What You’re Asking (and Yes — We Agree)

You are essentially requesting:

  • Activity auto-launch when page selected

  • Script automatically opening matching TV card

  • Media player selection triggering activity logic

  • Direct support for HA Button entities without wrapper scripts

These are very valid requests, and they align with where Astrion is evolving.

Internally we are working toward:

:white_check_mark: Activity-centric workflow
:white_check_mark: Context-aware card switching
:white_check_mark: Deeper Home Assistant entity support
:white_check_mark: Reduced need for “wrapper scripts”

Your use case (multi-stage switching + receiver-centric theatre) is exactly the scenario driving these improvements.


:star: Pro Tip (Used by Advanced Users)

Many experienced users adopt this flow:

Activities Page → Control Page → Never Go Back

Once an activity is started, daily use happens only inside the TV card.
The Activities page becomes similar to a traditional remote’s “Home” button.


:speech_balloon: Community Input

We’d also love to hear how other users with Node-RED or multi-zone setups structure their dashboards — there isn’t a single correct method yet, and shared patterns help everyone.

Thanks again for the fantastic write-up and welcome aboard — you’re already operating Astrion at “power-user level.” :slightly_smiling_face:

— Charles
Sanytron / Astrion Team

Thanks, So I guess a major recommendation would be some way to command the remote from HA, so that when an activity script is called, it can also tell the remote to go to the right page/TV card for that activity. Is there an API for the remote that can trigger that, or will we have to wait for future software updates to add either a feature to switch to a certain TV card when a script is called, or inversely, fire a script when a TV card is selected, or even fire a script when a page is selected then you could have one page per activity with all required devices and controls separated.

Or alternatively, allow HA to force the remote to a certain configuration from a script, whether that be just opening a certain page, or bringing up a particular card specifically.

Either option wouldn’t get in the way of the current control paradigm of everything being separated and modular, but would allow for automated activity flows for those that want to have them work that way too.

At the moment it looks like I would either need one massive page with all the activities and TV cards on it, press a button to fire off the activity then scroll down to find the matching TV card to have the controls work. or I’d need one page for the activity buttons, and then switch pages to the one with the cards required manually. either way that’s a lot of fingering for each activity.

Actually just digging deeper (much deeper)

I could write a multi-state universal media player entity that changes commands based on the current state (set from the activity scripts) but that is a fair bit more work with my setup having so many states. in Roscard this would be one master TV control entity if it supports the media_player entity being changed on the fly, or at least reduce it from 10+ TV cards down to two or three.

Might also be possible to have every button in a master TV card be scripted with a state check to determine which device the control would go do. but again, I dont know if i can swap out the media_player entity on the fly to have the on-screen controls work instead of just the D-Pad and volume controls etc.

Yes button.* and switch.* entities being able to be used directly in the TV ROSCard would definitely make things easier. I find wrapping these entities (especially switch.*) in a script causes unwanted behaviour like continual power cycling.

DO NOT wrap any of Activity switch.* entities in a script. Currently I rely on the voice assistant feature to switch my Activities, this is the most reliable way. The voice assistant feature on the remote is actually quite good.

  • I also have quite a comprehensive NodeRED flow to control my components and Harmony Hub for IR control.
  • *.button entities I currently wrap in a script, my NodeRED Flow will automatically route the button presses for the correct activity.

Astrion needs to be able to select from a select list of Activities to make Harmony work correctly. This needs to be an extra option in the TV ROSCard that will change the power behaviour to a input_select list rather than just a power ON/OFF Switch.

I have provided the importable NodeRED flows below

Flow for button.* entities to route to the correct Harmony Activity

Harmony Hub Button Mapper & Home Assistant

  • Ethos behind this is it uses one button.* entity mapped to the remote, NodeRED does the Activity detection and routes the button presses to the right device.

Flow for ethernet (RS232) control of NAD Amplifier

  • Ethos behind this is button.* entities directly control volume, surround sound mode and starting volume of the amplifier

NAD RECEIVER AMPLIFIER, RS232 TCP (ETHERNET) CONTROL & HOME ASSISTANT OUTPUT SENSORS.

Flow for voice control switches that switch between Harmony Activities

  • These now control HA Assist not just Google Assistant
  • Activity switching between Harmony activities works well with voice control on this one. I usually request “Turn TV ON”, “Turn TV OFF”, “Turn DVD ON”, Turn DVD OFF” when switching but it will directly switch between activities.
[{"id":"c37b651550628148","type":"group","z":"fbc96b6cec386837","name":"TV Switch for Google Assistant","style":{"label":true},"nodes":["5b6864f187884333","27a6954d539c7a10","d56b0577d5fc2d33","3a65669303d1dbf8","ea33ada073e6af47","e8430ca818da53ee","5f3ec2aacb65b008"],"x":1594,"y":3299,"w":552,"h":242},{"id":"5b6864f187884333","type":"H activity","z":"fbc96b6cec386837","g":"c37b651550628148","name":"Turn ON Shield","server":"21dd4f96b9a6b12b","activity":"48067014","label":"SHIELD TV","x":1880,"y":3340,"wires":[[]]},{"id":"27a6954d539c7a10","type":"H activity","z":"fbc96b6cec386837","g":"c37b651550628148","name":"Harmony OFF","server":"21dd4f96b9a6b12b","activity":"-1","label":"PowerOff","x":1880,"y":3380,"wires":[[]]},{"id":"d56b0577d5fc2d33","type":"ha-switch","z":"fbc96b6cec386837","g":"c37b651550628148","name":"TV","version":0,"debugenabled":false,"inputs":0,"outputs":2,"entityConfig":"fe79d7fb9b892728","enableInput":false,"outputOnStateChange":true,"outputProperties":[{"property":"outputType","propertyType":"msg","value":"state change","valueType":"str"},{"property":"payload","propertyType":"msg","value":"string","valueType":"entityState"}],"x":1670,"y":3360,"wires":[["5b6864f187884333"],["27a6954d539c7a10"]]},{"id":"3a65669303d1dbf8","type":"trigger-state","z":"fbc96b6cec386837","g":"c37b651550628148","name":"Shield TV ON","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"SHIELD TV"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":1690,"y":3440,"wires":[["5f3ec2aacb65b008"],[]]},{"id":"ea33ada073e6af47","type":"trigger-state","z":"fbc96b6cec386837","g":"c37b651550628148","name":"Harmony Power_OFF","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"power_off"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":1720,"y":3500,"wires":[["e8430ca818da53ee"],[]]},{"id":"e8430ca818da53ee","type":"api-call-service","z":"fbc96b6cec386837","g":"c37b651550628148","name":"TV SWITCH OFF","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.tv"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_off","x":2030,"y":3500,"wires":[[]]},{"id":"5f3ec2aacb65b008","type":"api-call-service","z":"fbc96b6cec386837","g":"c37b651550628148","name":"TV SWITCH ON","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.tv"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_on","x":2020,"y":3440,"wires":[[]]},{"id":"21dd4f96b9a6b12b","type":"harmony-server","name":"Living Room Hub","ip":"192.168.1.252"},{"id":"fe79d7fb9b892728","type":"ha-entity-config","server":"bb380e72.b8825","deviceConfig":"e3d65fbe01b3b8d9","name":"TV","version":"6","entityType":"switch","haConfig":[{"property":"name","value":"TV"},{"property":"icon","value":"phu:nvidia-shield"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"switch"}],"resend":false,"debugEnabled":false},{"id":"bb380e72.b8825","type":"server","name":"Home Assistant","version":6,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":["y","yes","true","on","home","open"],"connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"e3d65fbe01b3b8d9","type":"ha-device-config","name":"TV","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""},{"id":"778dac2c6d1894e1","type":"group","z":"fbc96b6cec386837","name":"Record Player Switch for Google Assistant","style":{"label":true},"nodes":["9b34b18fa6a58e0c","445985655571c0d2","2ea0c9698faebb6c","2b21cf6aa173a74d","aacaf123d4167033","ccbf84cdc0e6c256","f2a7e1a71bb9a0a2"],"x":874,"y":3639,"w":652,"h":242},{"id":"9b34b18fa6a58e0c","type":"H activity","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"Turn ON Record Player","server":"21dd4f96b9a6b12b","activity":"39069297","label":"Play Vinyl","x":1190,"y":3680,"wires":[[]]},{"id":"445985655571c0d2","type":"H activity","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"Harmony OFF","server":"21dd4f96b9a6b12b","activity":"-1","label":"PowerOff","x":1160,"y":3720,"wires":[[]]},{"id":"2ea0c9698faebb6c","type":"ha-switch","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"Record Player","version":0,"debugenabled":false,"inputs":0,"outputs":2,"entityConfig":"57b89ad6209b60ea","enableInput":false,"outputOnStateChange":true,"outputProperties":[{"property":"outputType","propertyType":"msg","value":"state change","valueType":"str"},{"property":"payload","propertyType":"msg","value":"string","valueType":"entityState"}],"x":970,"y":3700,"wires":[["9b34b18fa6a58e0c"],["445985655571c0d2"]]},{"id":"2b21cf6aa173a74d","type":"trigger-state","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"Play Vinyl ON","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"Play Vinyl"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":970,"y":3780,"wires":[["f2a7e1a71bb9a0a2"],[]]},{"id":"aacaf123d4167033","type":"trigger-state","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"Harmony Power_OFF","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"power_off"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":1000,"y":3840,"wires":[["ccbf84cdc0e6c256"],[]]},{"id":"ccbf84cdc0e6c256","type":"api-call-service","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"RECORD PLAYER SWITCH OFF","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.record_player"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_off","x":1360,"y":3840,"wires":[[]]},{"id":"f2a7e1a71bb9a0a2","type":"api-call-service","z":"fbc96b6cec386837","g":"778dac2c6d1894e1","name":"RECORD PLAYER SWITCH ON","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.record_player"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_on","x":1350,"y":3780,"wires":[[]]},{"id":"57b89ad6209b60ea","type":"ha-entity-config","server":"bb380e72.b8825","deviceConfig":"7e50d0c1b174e8f7","name":"Record Player","version":"6","entityType":"switch","haConfig":[{"property":"name","value":"Record Player"},{"property":"icon","value":"mdi:record_player"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"switch"}],"resend":false,"debugEnabled":false},{"id":"7e50d0c1b174e8f7","type":"ha-device-config","name":"Record Player","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""},{"id":"e09463b843f53cd3","type":"group","z":"fbc96b6cec386837","name":"DVD Switch for Google Assistant","style":{"label":true},"nodes":["1801db280d6ce79d","4ef610b56284fd80","6b57d235d028d9b8","c2ba4391fb0032d8","1dbe4aa21f4877f1","17fdd8d947e37ca7","a18a7207411e2f71"],"x":1614,"y":3639,"w":552,"h":242},{"id":"1801db280d6ce79d","type":"H activity","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"Turn ON DVD","server":"21dd4f96b9a6b12b","activity":"30822006","label":"OPPO Bluray DVD","x":1900,"y":3680,"wires":[[]]},{"id":"4ef610b56284fd80","type":"H activity","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"Harmony OFF","server":"21dd4f96b9a6b12b","activity":"-1","label":"PowerOff","x":1900,"y":3720,"wires":[[]]},{"id":"6b57d235d028d9b8","type":"ha-switch","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"DVD","version":0,"debugenabled":false,"inputs":0,"outputs":2,"entityConfig":"b5c641c8471ca970","enableInput":false,"outputOnStateChange":true,"outputProperties":[{"property":"outputType","propertyType":"msg","value":"state change","valueType":"str"},{"property":"payload","propertyType":"msg","value":"string","valueType":"entityState"}],"x":1690,"y":3700,"wires":[["1801db280d6ce79d"],["4ef610b56284fd80"]]},{"id":"c2ba4391fb0032d8","type":"trigger-state","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"OPPO Bluray DVD ON","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"OPPO Bluray DVD"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":1740,"y":3780,"wires":[["a18a7207411e2f71"],[]]},{"id":"1dbe4aa21f4877f1","type":"trigger-state","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"Harmony Power_OFF","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"power_off"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":1740,"y":3840,"wires":[["17fdd8d947e37ca7"],[]]},{"id":"17fdd8d947e37ca7","type":"api-call-service","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"DVD SWITCH OFF","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.dvd"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_off","x":2050,"y":3840,"wires":[[]]},{"id":"a18a7207411e2f71","type":"api-call-service","z":"fbc96b6cec386837","g":"e09463b843f53cd3","name":"DVD SWITCH ON","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.dvd"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_on","x":2050,"y":3780,"wires":[[]]},{"id":"b5c641c8471ca970","type":"ha-entity-config","server":"bb380e72.b8825","deviceConfig":"5608ffb005953ad8","name":"DVD","version":"6","entityType":"switch","haConfig":[{"property":"name","value":"DVD"},{"property":"icon","value":"phu:dvd"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"switch"}],"resend":false,"debugEnabled":false},{"id":"5608ffb005953ad8","type":"ha-device-config","name":"DVD","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""},{"id":"1cee3971a9b06a50","type":"group","z":"fbc96b6cec386837","name":"Normal TV Switch for Google Assistant","style":{"label":true},"nodes":["8a5b49a102c92b3a","fd8346e7327463d7","668d32dbd694622a","20d1c3148cea2d6c","c519995fc8b5f70c","67a3bb1a73073428","093e146cbd2c2e10"],"x":874,"y":3299,"w":592,"h":242},{"id":"8a5b49a102c92b3a","type":"H activity","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Turn ON Normal TV","server":"21dd4f96b9a6b12b","activity":"42679719","label":"Normal TV","x":1180,"y":3340,"wires":[[]]},{"id":"fd8346e7327463d7","type":"H activity","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Harmony OFF","server":"21dd4f96b9a6b12b","activity":"-1","label":"PowerOff","x":1160,"y":3380,"wires":[[]]},{"id":"668d32dbd694622a","type":"ha-switch","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Normal TV","version":0,"debugenabled":false,"inputs":0,"outputs":2,"entityConfig":"ecdc8ff7518bb0d8","enableInput":false,"outputOnStateChange":true,"outputProperties":[{"property":"outputType","propertyType":"msg","value":"state change","valueType":"str"},{"property":"payload","propertyType":"msg","value":"string","valueType":"entityState"}],"x":960,"y":3360,"wires":[["8a5b49a102c92b3a"],["fd8346e7327463d7"]]},{"id":"20d1c3148cea2d6c","type":"trigger-state","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Normal TV ON","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"Normal TV"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":980,"y":3440,"wires":[["093e146cbd2c2e10"],[]]},{"id":"c519995fc8b5f70c","type":"trigger-state","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Harmony Power_OFF","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"power_off"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":1000,"y":3500,"wires":[["67a3bb1a73073428"],[]]},{"id":"67a3bb1a73073428","type":"api-call-service","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Normal TV SWITCH OFF","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.normal_tv"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_off","x":1330,"y":3500,"wires":[[]]},{"id":"093e146cbd2c2e10","type":"api-call-service","z":"fbc96b6cec386837","g":"1cee3971a9b06a50","name":"Normal TV SWITCH ON","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.normal_tv"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_on","x":1330,"y":3440,"wires":[[]]},{"id":"ecdc8ff7518bb0d8","type":"ha-entity-config","server":"bb380e72.b8825","deviceConfig":"f03ec738168a5393","name":"Normal TV","version":"6","entityType":"switch","haConfig":[{"property":"name","value":"Normal TV"},{"property":"icon","value":"mdi:television-classic"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"switch"}],"resend":false,"debugEnabled":false},{"id":"f03ec738168a5393","type":"ha-device-config","name":"Normal TV","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""},{"id":"f4c939a297447b4d","type":"group","z":"fbc96b6cec386837","name":"TV Switch for Google Assistant","style":{"label":true},"nodes":["19f1aa8ce3208d8e","d6b31d05f74558b6","d3587d77de3887eb","ef8ce856b3840815","b54a6bab1e63fa74","e6145ac6e38f9c79","794d254c70093e34"],"x":2214,"y":3299,"w":572,"h":242},{"id":"19f1aa8ce3208d8e","type":"H activity","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"Turn ON Nintendo Switch 2","server":"21dd4f96b9a6b12b","activity":"53662309","label":"Nintendo Switch 2","x":2540,"y":3340,"wires":[[]]},{"id":"d6b31d05f74558b6","type":"H activity","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"Harmony OFF","server":"21dd4f96b9a6b12b","activity":"-1","label":"PowerOff","x":2500,"y":3380,"wires":[[]]},{"id":"d3587d77de3887eb","type":"ha-switch","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"Nintendo","version":0,"debugenabled":false,"inputs":0,"outputs":2,"entityConfig":"9a2e6ea3b691b804","enableInput":false,"outputOnStateChange":true,"outputProperties":[{"property":"outputType","propertyType":"msg","value":"state change","valueType":"str"},{"property":"payload","propertyType":"msg","value":"string","valueType":"entityState"}],"x":2300,"y":3360,"wires":[["19f1aa8ce3208d8e"],["d6b31d05f74558b6"]]},{"id":"ef8ce856b3840815","type":"trigger-state","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"Nintendo Switch 2 ON","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"Nintendo Switch 2"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":2340,"y":3440,"wires":[["794d254c70093e34"],[]]},{"id":"b54a6bab1e63fa74","type":"trigger-state","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"Harmony Power_OFF","server":"bb380e72.b8825","version":5,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["select.harmony_hub_activities"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"power_off"}],"customOutputs":[],"outputInitially":true,"stateType":"str","enableInput":false,"x":2340,"y":3500,"wires":[["e6145ac6e38f9c79"],[]]},{"id":"e6145ac6e38f9c79","type":"api-call-service","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"TV SWITCH OFF","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.nintendo"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_off","x":2650,"y":3500,"wires":[[]]},{"id":"794d254c70093e34","type":"api-call-service","z":"fbc96b6cec386837","g":"f4c939a297447b4d","name":"Nintendo SWITCH ON","server":"bb380e72.b8825","version":7,"debugenabled":false,"action":"switch.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["switch.nintendo"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"switch","service":"turn_on","x":2660,"y":3440,"wires":[[]]},{"id":"9a2e6ea3b691b804","type":"ha-entity-config","server":"bb380e72.b8825","deviceConfig":"c3d07af1c382fa6b","name":"Nintendo","version":6,"entityType":"switch","haConfig":[{"property":"name","value":"Nintendo"},{"property":"icon","value":"phu:nintendo-switch-logo"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"switch"}],"resend":false,"debugEnabled":false},{"id":"c3d07af1c382fa6b","type":"ha-device-config","name":"Nintendo","hwVersion":"","manufacturer":"Node-RED","model":"Nintendo","swVersion":""},{"id":"4e09994d44924ee9","type":"global-config","env":[],"modules":{"node-red-contrib-harmony-extra":"1.0.1","node-red-contrib-home-assistant-websocket":"0.80.3"}}]

Some great insight there, thanks. I dont use any voice control, I find it a step backwards from a good button based interface but that’s just me, i do want to set up a local voice assistant in HA though.

I could replicate something like you have with NR handling generic inputs for each control on the remote and routing to the correct device based on context of the current activity. I would like to take that a step further with a dynamic media entity to get artwork and such on the screen, that’s a bit more complex but very possible with HA and NR.

I currently only use my NR flows for controls for Power and inputs, but also volume, mute, audio processing modes, some IR emitter controls for one of the TV tuner boxes so I could expand those out to the rest of the system. my video matrix has an IP addressable IR emitter built in so I can send any hex commands to any source device or display in the house but I’m not really using that at the moment for anything but a couple of backup IR power commands for TVs prone to IP control hiccups that work fine once powered on.

My logic flows are a bit more complex (though not as neat, despite my heavy use of link nodes..) and i suspect if i committed to redoing them from scratch they could be a lot simpler, I have a lot of things that are obviously being checked twice or set by multiple flows redundantly, but I like the behaviour I have where if I turn on any device manually, or change any input manually, the rest will turn on and configure as required, everything is connected and aware of everything else, handy if for example the TV dropped from the network after a software update and ignores the IP power on command, there’s an IR backup that will turn it on if the flow detects no change in power draw. or if I manually turn on the CD player it will set the receiver to that input, set it to stereo mode, then set the current room state to match that so that everything could stay in sync, the old remote even set itself to the right activity so all the controls were set. I’ve been working on another flow where if someone casts to the theatre shield without the room being on or setup it will detect that and configure everything so it “just works”, handy for family and guests that dont know which button to press.

I prefer button controls as well. Unfortunately as you have found it is quite complex to achieve with the current version of Astrion.

I have three HA Voice PE’s in my house and use Open AI Conversation Agent but I mainly use the Astrion if I am going to use voice control (usually faster than searching the remote for the right control). I assume you could setup a local voice assistant on your current setup and use the Astrion hardware and physical mic button to test the voice capabilities.

I only use voice because a switch.* wrapped in a script. does not behave well. Sooner Astrion enables direct control of switch.^ and button. entities from the ROS TV Card, the better.

Made some good progress today, I now have one single TV card that can show an activity selector as an input/app selector, which triggers a script, then the button presses are automatically directed to the correct device by NodeRed, this also works if I change the current activity from another device such as my wall-mounted LCD control panel, or a voice command for example.

I cant seem to find a way to get the display to show the current “input” in any way nor can I see a media player entity without changing the remote over to it manually and then losing control. this seems like a major feature lacking.. the TV card should support a media entity with all the extra screen space available if we could hide the usually duplicate power and ± buttons.

Currently I only have a shield (using Android Remote and ADB commands) and a Zidoo Z2600 using a HACS integration with HA.

Next I need to get the rest of the activities hooked up to my existing flows and my IR emitter set up to send commands for “dumb” devices like my CD player etc, but this is a good start.

Then I need to figure out how to get custom graphics to those inputs.. spotify seems automatically there without doing anything so that must be possible.

1 Like

Hi Faceman :waving_hand:

Really good progress on your side — and this is exactly the kind of “real system” usage that exposes where the TV ROS Card still needs to mature.

A few thoughts from our side:


:brain: 1. Your architecture (NodeRed + HA + activity routing)

What you’ve built is actually very aligned with how power users end up structuring advanced setups:

  • HA = state + entity source of truth

  • NodeRed = routing / orchestration layer

  • Astrion = interaction layer (input surface)

The “single activity card + context-aware routing” approach is especially strong — and we’re seeing more integrators converge on this pattern.


:television: 2. Missing “current input / activity state” visibility

You’re absolutely right here — this is currently a gap.

Right now:

  • TV card can send commands

  • but cannot reliably reflect unified activity/input state across different entity types

This becomes especially noticeable when mixing:

  • media_player

  • remote

  • ADB / Android TV

  • vendor-specific integrations (Zidoo, etc.)

We are actively working on a unified “activity state model”, where:
:backhand_index_pointing_right: the UI always knows what the system believes is active, regardless of backend entity type

This will allow:

  • persistent input display

  • cross-device state sync

  • no need to “switch mode” just to see correct media state


:control_knobs: 3. Media entity inside TV card (your key point)

This is a very valid limitation you’ve identified:

Right now the TV card is:

  • control-focused

  • not media-state-rich

What you’re asking for is essentially:
:backhand_index_pointing_right: “media-aware TV card with dynamic state + artwork + actions”

This is exactly where we are heading, including:

  • media artwork display

  • active source highlighting

  • dynamic control sets (show/hide power or transport depending on context)

  • tighter media_player integration without forcing mode switching


:counterclockwise_arrows_button: 4. NodeRed routing vs native mapping

What you built (central routing layer) is powerful, but it also highlights something important:

We don’t want users to need NodeRed just to achieve:

  • correct device targeting

  • activity-based control

  • context-aware button routing

So part of our roadmap is:
:backhand_index_pointing_right: moving this intelligence closer to the ROS Card / HA entity abstraction layer
so NodeRed becomes optional, not required


:framed_picture: 5. Custom graphics for inputs

Yes — this is already partially visible in systems like Spotify because:

  • metadata is exposed via media_player capabilities

  • artwork comes from the entity state

We are extending this idea so that:

  • any “activity” or “input” entity can optionally expose:

    • icon

    • artwork

    • label grouping

    • UI override assets

So your idea is correct:
:backhand_index_pointing_right: inputs should not be “plain buttons”, they should be “visual entities”


:compass: 6. Core takeaway

What you’re describing is basically the next evolution of the TV ROS Card:

From:

command interface (scripts + entities)

To:

stateful media/activity control surface

That shift includes exactly what you identified:

  • persistent input state

  • media-aware layout

  • entity-type unification

  • reduced dependency on external routing logic


:+1: Final note

Honestly, your setup is already at the “edge case / power integrator” level, and you’re effectively surfacing the exact missing primitives we need to fix next.

This kind of feedback is extremely valuable because it’s not theoretical — it’s coming from a real multi-device orchestration system.

Keep it coming :+1:

1 Like

I now have some more advanced functionality working at a “beta” level.

A Universal Media Entity is mostly functioning, it automatically changes its data sources from Zidoo to Shield to Kodi etc based on the selected activity. This mostly works, in HA it updates instantly and smoothly, but on the remote it seems to update much slower, with artwork and media info not updating very well unless you exit the card or reload the config , especially when the media card is selected and on screen. I have also found a bug with the progress timer resetting to 0 seconds (or the last time manually skipped to via touching the progress bar) every time the card is brought up on screen. That’s definitely a bug since the progress bar in HA works just fine. I need to add some more details to that media entity to map volume and other details, also when selecting a source where a dummy media entity (custom dummy entities for dumb devices like CD player and Record Player) does not set placeholder or blank “artist” “track” etc details the remote keeps displaying the last ones it received. If anyone tries this, you need to manually send blank data, not NULL for the remote to erase what was on screen, it ignores NULL values, which is perfectly understandable.

I have that set to come up on screen when pressing the “music” shortcut button and hides when pressed again, but when that card is on screen the remote buttons stop working because only the TV card has remote buttons available for mapping.. again this seems like an arbitrary limitation and we should be able to either map remote buttons for media entities, or when quickly bringing up a card from a shortcut button (light,cover,music,climate, etc) there should be an option to keep the button mapping of the TV card that was selected prior, treat those as shortcuts that only temporarily display on the screen but dont override the physical button functions of the TV card “behind” them.

On the Nodered side for the node-nerds:

Initial configuration example with generic button triggers (booleans) being routed to each device based on the current selected activity (flow.currentmode variable set elsewhere) on a per-button basis, would be hundreds of nodes and lots of functions still yet to map, very easy to get started this way but too complex once all buttons and controls are hooked up:

The outputs of each of those nodes would map to each individual sources control, would be hundreds of nodes and links when all devices are configured.

Some devices talk different languages, for example the shield uses {“command”:“KEYCODE_DPAD_LEFT”}, but the Zidoo uses {“command”:“Key.Left”}, Kodi (used for music playback only in my case) uses {“method”:“Input.Left”}, so it would be easier to use activity nodes with {“command”:“{{payload}}” or {“method”:“{{payload}}”} and change the payloads dynamically. This reduces the activity nodes down to one per device, or at least, one per action type per device, some devices would need a couple of nodes with different data formatting.

So that means redoing the big bulky boolean switchers for every button button down to one switcher, that switches msg.payloads containing the button name, then translate those generic names to the ones the devices expect. That looks like this and is significantly cleaner. These controls never need to handle multiple simultaneous button inputs so I will stick with this method. They work absolutely perfectly so far and there is zero noticeable delay. this also allows me to punch through buttons directly to devices if needed, for volume/mute for example.
I still plan to rewrite this so that the button command is in “msg.command” and the target device is carried, per message, in “msg.target” rather than having one target device selected by flow.currentmode, I would then be able to have multiple target devices in each activity, much more flexible if for example some activities use a separate volume target, like a zone 2, or for expanding out to multi-room control.

Infrared controls use a function node that contains all the IR codes per button, they are routed in the same way, a generic button press is routed to the active device, those generic names are translated into the matching command names for clarity, then the function node outputs the IR code that matches the incoming message, in the case of the CD player I map the select button (dpad center) to play/pause, back to stop, menu to eject, left and right will be FF/RW and channel up/down will be track skip buttons once I get them linked up too.

Ideally however I would have a custom card on the remote screen for the CD player that puts some of those controls on screen with appropriate icons for Eject, stop, repeat, shuffle etc.

My previous Sofabaton X1S setup is still running alongside this just fine as I have to use it for certain things until the Astrion gets more updates, but doing this project is making me better at Node-Red and HA and better at writing scripts and functions myself so I can see a LOT of things i could have done better,

1 Like