Trying to work with 2 separate lists of received data from arduino

You also need to check the place where you keep the relay status response in AI2, presumably one of those Label.Text values.

If you will be powering down the app, you will also need to capture incoming values in TinyDB, under the same unique tags you use to look up Labels in your dictionary.

None of this would help you if a relay changes state unbeknownst to your app.

Yes I added labels to hold the state. Ideally I will load them one time only when the app loads, before the "when relay changed" parts comes into play, but I dont see any way of doing that.

There is also the slight problem the Arduino can control the relays according to the program it's running.

A possible solution would be to have the switches only display the state and add another button for eacht relay to operate it. But adding a button to operate a switch doesn't really sit right with me.

I had to run a little experiment to see how the switch changed event works.
on and off testing

In the changed event, the value of the switch.On attribute reflects the result of the user changing the switch.

So you can test the .On attribute and decide what message you want to send ("turn it on" vs turn it off".)

Changing the .On attribute value under program control should not trigger this event, according to the docs.

Another idea would be to not have Labels for the relay states, but only switches.

That would require a second dictionary, mapping the relay tags into switches.

The incoming message would need to have its prefix checked (is in list) against the keys list of the Labels dictionary and against the Switches dictionary, to see which should be updated in an appropriate branch of the if/then test.

The switch documentation lied.
on and off testing 2

The switch changed event fires even if the change is done programmatically, so you are right to be worried about cascade effects.

That's another reason I dislike checkboxes and switches.

A Button with On/Off text to remember relay state would not have that problem.
Changing the Button.Text would not trigger a Click event.

I thought of another alternative way to handle switch logic, augmented by a current TinyDB record of state information received via Bluetooth.

This assumes incoming BT messages are being logged into TinyDB by tag, before updating switch states.

Before updating a switch state as a result of an incoming message, check if its current state differs from TinyDB's record.
If it is the same, don't bother updating it, otherwise change the switch state.

In the switch changed event, check if the new switch state matches the TinyDB record.
If the values match, don't bother sending a BT command.
Otherwise, send the BT command that asks for the relay to match the new on/off state.

If a relay falls out of sync, toggling the AI2 switch a couple of times should bring it (and TinyDB) back into sync.

I don't htink that would work with TinyDB. The relays are switched mutliple times a day and the app only connects once every few days. The current state of all 8 relays is almost garuenteed not to match the TinyDB stored values.

For now I've added buttons below the switches. The switches only indicate the status of the relay and pressing the button sends the command to change it. No feedback loop.

Ugly solution but it will have to do, for now.