CECToggleState logic is flawed

The logic for the CECToggleState builtin seems flawed to me. If Kodi is active and one calls CECToggleState StandbyDevices() is called putting the TV/AVR in standby. That’s good.

If CECToggleState is called when a different CEC device is active ActivateSource() is called, switching the TV/AVR to CoreELEC. That’s good.

If CECToggleState is called when the TV/AVR are in standby one of two things can happen.

If CoreELEC was not active when the TV/AVR went into standby ActivateSource() is called, turning the TV/AVR on and switching the input to CoreELEC. That’s good.

If CoreELEC was active when the TV/AVR went into standby StandbyDevices() is called. So nothing happens. That’s bad. It’s not really a toggle at this point.

The code logic is in xbmc/xbmc/peripherals/devices/PeripheralCecAdapter.cpp

if (m_cecAdapter->IsLibCECActiveSource() &&
(mode == STATE_SWITCH_TOGGLE || mode == STATE_STANDBY))
{
CLog::Log(LOGDEBUG, “{} - putting CEC device on standby…”, FUNCTION);
StandbyDevices();
return false;
}

It doesn’t check that the devices aren’t already in standby, in which case ActivateSource() should be called instead of StandbyDevices(). I’m not sure what call could check if the devices are already in standby or if there is such a call. If there is the logic fix is pretty easy.

There is a setting in the CEC peripheral to issue “Inactive Source” on shutdown. That call should probably be made anytime StandbyDevices() is called. That would at least fix cases when CoreELEC actually issues the standby command to the TV/AVR, but it wouldn’t fix say the case of the TV being turned off by it’s own remote.

If this logic was fixed mapping the power button on a remote to CECToggleState would make CoreELEC act more like android boxes where the device stays powered up and ready but the TV/AVR are toggle on/off. (Yes, some some/pause play logic would be necessary too.)

As it is it can really only reliably turn the TV/AVR off using CECToggleState.

Would anyone happen to know a called to check if the devices are already in standby?

I think the issue is actually in ProcessStandbyDevices.

Why have the else clause below? This is only called when CoreELEC (kodi) is going inactive. So it should mark itself inactive regardless of whether or not it is sending power off commands to the TV/AVR. It would still be protected by the Set Inactive user preference for people who have chosen to disable it.

    if (!m_configuration.powerOffDevices.IsEmpty())
    {
      m_standbySent = CDateTime::GetCurrentDateTime();
      m_cecAdapter->StandbyDevices(CECDEVICE_BROADCAST);
    }
    else if (m_bSendInactiveSource == 1)
    {
      CLog::Log(LOGDEBUG, "{} - sending inactive source commands", __FUNCTION__);
      m_cecAdapter->SetInactiveView();
    }

Of course I decided to look at this and build CoreElec for the first time right when the xz repository has been pulled from github. :stuck_out_tongue:

Removing the else does make CECToggleState work more as expected. It can now wake the TV/AVR from standby. There are a couple of catches though.

As expected if the TV is shut off by some means other than a kodi CEC command kodi is still in the active source state. The CECToggleState must be sent twice to turn on the TV/AVR. Better than not being able to turn it on.

The part I don’t get is that when I trigger it via a remote button when toggling from off to on the key gets processed twice.

Here’s the log from turning things off (active → standby)

2024-04-04 23:30:04.047 T:4418    debug <general>: CLibInputKeyboard::ProcessKey - using delay: 500ms repeat: 33ms
2024-04-04 23:30:04.047 T:5991    debug <general>: Thread Timer start, auto delete: false
2024-04-04 23:30:04.068 T:4413    debug <general>: Keyboard: scancode: 0x3, sym: 0x32, unicode: 0x32, modifier: 0x0
2024-04-04 23:30:04.068 T:4413    debug <general>: HandleKey: action CECToggleState [122], toggling state of playing device
2024-04-04 23:30:04.068 T:4413    debug <general>: ToggleDeviceState - putting CEC device on standby...
2024-04-04 23:30:04.070 T:4477    debug <general>: ProcessStandbyDevices - sending inactive source commands
2024-04-04 23:30:04.070 T:4476    debug <general>: CecLogMessage - marking Playback 2 (8) as inactive source
2024-04-04 23:30:04.070 T:4476    debug <general>: CecLogMessage - >> source deactivated: Playback 2 (8)
2024-04-04 23:30:04.110 T:4476    debug <general>: CecLogMessage - << Playback 2 (8) -> broadcast (F): inactive source
2024-04-04 23:30:04.110 T:4476    debug <general>: CecLogMessage - << 80:9d:34:00
2024-04-04 23:30:04.197 T:5991    debug <general>: Thread Timer 3535782400 terminating
2024-04-04 23:30:04.250 T:4413    debug <general>: Keyboard: scancode: 0x3, sym: 0x32, unicode: 0x32, modifier: 0x0

Here’s from turning things on (standby->active)

2024-04-04 23:31:42.666 T:4418    debug <general>: CLibInputKeyboard::ProcessKey - using delay: 500ms repeat: 33ms
2024-04-04 23:31:42.666 T:6022    debug <general>: Thread Timer start, auto delete: false
2024-04-04 23:31:42.671 T:4413    debug <general>: Keyboard: scancode: 0x3, sym: 0x32, unicode: 0x32, modifier: 0x0
2024-04-04 23:31:42.671 T:4413    debug <general>: HandleKey: action CECToggleState [122], toggling state of playing device
2024-04-04 23:31:42.672 T:4413    debug <general>: ToggleDeviceState - waking up CEC device...
2024-04-04 23:31:42.672 T:4413    debug <general>: HandleKey: two (0xf032) pressed, window 10004, action is CECToggleState
2024-04-04 23:31:42.672 T:4413    debug <general>: ToggleDeviceState - waking up CEC device...
2024-04-04 23:31:42.675 T:4476    debug <general>: CecLogMessage - making Playback 2 (8) the active source
2024-04-04 23:31:42.675 T:4476    debug <general>: CecLogMessage - marking TV (0) as inactive source
2024-04-04 23:31:42.675 T:4476    debug <general>: CecLogMessage - >> source activated: Playback 2 (8)
2024-04-04 23:31:42.675 T:4476    debug <general>: CecLogMessage - sending active source message for 'Playback 2'
2024-04-04 23:31:42.675 T:4476    debug <general>: CecLogMessage - << powering on 'TV' (0)
2024-04-04 23:31:42.676 T:4476    debug <general>: CecLogMessage - << 80:04
2024-04-04 23:31:42.732 T:4476    debug <general>: CecLogMessage - << Playback 2 (8) -> broadcast (F): active source (3400)
2024-04-04 23:31:42.732 T:4476    debug <general>: CecLogMessage - << 8f:82:34:00
2024-04-04 23:31:42.826 T:6022    debug <general>: Thread Timer 3535782400 terminating
2024-04-04 23:31:42.849 T:4476    debug <general>: CecLogMessage - << Playback 2 (8) -> TV (0): menu state 'activated'
2024-04-04 23:31:42.849 T:4476    debug <general>: CecLogMessage - << 80:8e:00
2024-04-04 23:31:42.854 T:4413    debug <general>: Keyboard: scancode: 0x3, sym: 0x32, unicode: 0x32, modifier: 0x0

Why is there a second HandleKey entry? It can cause things to immediately turn back off. I have no idea why one way gets one HandleKey event and the other two.

I’m playing with this as I’m using an Ugoos AM6b+ which has an encrypted bootloader so no BL301 injection. And it’s recovery from standby via CEC/external remotes it not very reliable.

Anyone else have a ready to go solution for this situation?

I have home assistant send WOL packet when my receiver powers on.

AM6 suspends when tv powers off.

1 Like

Thanks for the suggestion @rome1931. I don’t have a home assistant setup though. So I decided to just drop CECToggleState and use CECActivateSource and CECStandby tied to a single key with longpress meaning turn off. Ugoos set to stop play when TV turns off rather than go to standby.

1 Like

Yes
Otherwise
EDID breaks and you have no display

Same as changing input at avr…

There is an unwritten rule
Must power on avr/TV first
Then CoreELEC machine

I just used the remote that came with the box, and got the Dune HD Premium remote and then programmed the additional buttons with my Logitech Harmony remotes. Only the UT-01 remote’s red power button seemed to be able to power the device on or wake from standby reliably enough. I set that to the resume instead of power. I set the second power button on that remote to be set explicitly to suspend, along with the power button on the Dune remote. That way I could set discrete power on and power off buttons, (which ime tend to be more reliable with Harmony remotes). Then set the Dune TV power to be the power menu button.

That works pretty consistently for me now.

I’ve never cared much for CEC beyond audio syncing and tbf it’s caused quite a bit of headache over the years…

1 Like

this is a good idea - right now I’m just having the device stop all media playback upon CEC off and leaving it on all the time but better to save the (albeit minimal) amount of power

1 Like