Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions Dynamic_RDS.php
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,18 @@ function DynRDSFastUpdate() {
$.get('api/plugin/Dynamic_RDS/FastUpdate');
}

// The RDS Settings group shares a single callback, but only the Style Text and
// Update Rate fields are re-read by the Engine on UPDATE. Everything else in the
// group is consumed when the transmitter is set up, so it still needs an FPP restart.
var DynRDSLiveRDSSettings = ['DynRDSPSStyle', 'DynRDSRTStyle',
'DynRDSPSUpdateRate', 'DynRDSRTUpdateRate'];

function DynRDSRDSSettingsUpdate(key) {
if (DynRDSLiveRDSSettings.indexOf(key) !== -1) {
DynRDSFastUpdate();
}
}

function DynRDSPiBootUpdate(key) {
$.post('api/plugin/Dynamic_RDS/PiBootChange/' + encodeURIComponent(key),
JSON.stringify(Object.assign({}, pluginSettings)));
Expand Down Expand Up @@ -519,15 +531,13 @@ function ScriptStreamProgressDialogDone() {
* Display all settings groups
*/
function displaySettingsGroups(array $settings): void {
PrintSettingGroup("DynRDSRDSSettings", getRDSStyleGuideHTML(), "", 1, "Dynamic_RDS", "UpdateDynRDSTransmitterChildren");
PrintSettingGroup("DynRDSRDSSettings", getRDSStyleGuideHTML(), "", 1, "Dynamic_RDS", "DynRDSRDSSettingsUpdate");

PrintSettingGroup("DynRDSTransmitterSettings", "", "", 1, "Dynamic_RDS", "DynRDSTransmitterFrequencyUpdate");

// Wrap Audio in div id to make it easy to hide/show for Si4713
echo '<div id="DynRDSAudioSettingsWrapper">';
PrintSettingGroup("DynRDSAudioSettings", "",
"<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1'></i>indicates a live change to transmitter, no FPP restart required",
1, "Dynamic_RDS", "DynRDSFastUpdate");
PrintSettingGroup("DynRDSAudioSettings", "", "", 1, "Dynamic_RDS", "DynRDSFastUpdate");
echo '</div>';

PrintSettingGroup("DynRDSPowerSettings", "", "", 1, "Dynamic_RDS", "DynRDSPiBootUpdate");
Expand Down
39 changes: 32 additions & 7 deletions Dynamic_RDS_Engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def read_config():
config['DynRDSQN8066BufferGain'] = totalGain % 18 // 3

if not (os.path.exists('/bin/mpc') or os.path.exists('/usr/bin/mpc')):
config['DynRDSmpcEnable'] = 0
config['DynRDSmpcEnable'] = '0'

logging.getLogger().setLevel(config['DynRDSEngineLogLevel'])
logging.info('Config %s', config)
Expand Down Expand Up @@ -294,13 +294,24 @@ def excessive(msg, *args, **kwargs):
writeStatus()

elif line == 'UPDATE':
priorMPCEnable = config['DynRDSmpcEnable']
read_config()
mqtt.publish('config', json.dumps(config, indent=8))
if (transmitter is not None and transmitter.active):
for key in rdsValues:
rdsValues[key] = ''
# MPC only ever sources {T}, so toggling it while idle would otherwise leave
# the last polled title stale. During a playlist {T} comes from FPP media
# events, so leave it alone.
if config['DynRDSmpcEnable'] != priorMPCEnable and not activePlaylist:
rdsValues['{T}'] = ''
nextMPCUpdate = datetime.now()
if transmitter is not None:
# Buffers cache their delay at construction, so re-sync on config changes
# or the status panel and the chip disagree on the update rate
if getattr(transmitter, 'PS', None) is not None:
transmitter.PS.delay = int(config['DynRDSPSUpdateRate'])
transmitter.RT.delay = int(config['DynRDSRTUpdateRate'])
updateRDSData()
transmitter.update()
if transmitter.active:
transmitter.update()
writeStatus()

elif line == 'START':
Expand Down Expand Up @@ -376,9 +387,23 @@ def excessive(msg, *args, **kwargs):
if not activePlaylist and transmitter is not None and transmitter.active and config['DynRDSmpcEnable'] == "1" and datetime.now() > nextMPCUpdate:
logging.debug('Processing mpc')
nextMPCUpdate = datetime.now() + timedelta(seconds=12)
# TODO: Error handling might be needed here if the mpc execution has an issue

# TODO: Future idea to handle multiple fields from mpc, but I've not seen them used yet. [{A}%artist%][{T}%title%][{N}%track%]
mpcLatest = subprocess.run(['mpc', 'current', '-f', '%title%'], stdout=subprocess.PIPE, check=False).stdout.decode('utf-8').strip()
try:
mpcLatest = subprocess.run(['mpc', 'current', '-f', '%title%'],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
check=False, timeout=2,
encoding='utf-8', errors='replace').stdout.strip()
except subprocess.TimeoutExpired:
# mpc or MPD is wedged - back off so RDS group output isn't stalled every 12 seconds
logging.warning('mpc timed out - backing off for 60 seconds')
nextMPCUpdate = datetime.now() + timedelta(seconds=60)
mpcLatest = rdsValues['{T}']
except OSError as error:
logging.warning('mpc could not be run (%s) - backing off for 60 seconds', error)
nextMPCUpdate = datetime.now() + timedelta(seconds=60)
mpcLatest = rdsValues['{T}']

if rdsValues['{T}'] != mpcLatest:
rdsValues['{T}'] = mpcLatest
updateRDSData()
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ During the plugin install, an example script is copied to the FPP `media/scripts
All settings are on the plugin's config page, reachable from **Status/Control -> Dynamic RDS**. The page auto-detects your transmitter over I<sup>2</sup>C and hides the settings that don't apply to it.

> [!NOTE]
> Settings marked with a lightning bolt icon take effect immediately on the transmitter — no FPP restart needed.
> Settings marked with a lightning bolt icon take effect immediately — no FPP restart needed.

### RDS Settings
| Setting | Default | Notes |
Expand Down
28 changes: 15 additions & 13 deletions settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
"name": "DynRDSQN8066AmpPower",
"description": "Amp Power (0-100)",
"tip": "Adjust the power output for the amplifier after the transmitter chip. This is controlled by PWM output.",
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1'></i> - Controlled by PWM",
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i> - Controlled by PWM",
"restart": 0,
"reboot": 0,
"type": "number",
Expand Down Expand Up @@ -273,7 +273,7 @@
"max": 20,
"step": 1,
"default": 0,
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1'></i>"
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>"
},
"DynRDSQN8066SoftClipping": {
"name": "DynRDSQN8066SoftClipping",
Expand All @@ -284,7 +284,7 @@
"checkedValue": "1",
"uncheckedValue": "0",
"default": 1,
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1'></i>"
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>"
},
"DynRDSQN8066AGC": {
"name": "DynRDSQN8066AGC",
Expand All @@ -295,7 +295,7 @@
"checkedValue": "1",
"uncheckedValue": "0",
"default": 0,
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1'></i>"
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>"
},
"DynRDSSi4713TestAudio": {
"name": "DynRDSSi4713TestAudio",
Expand Down Expand Up @@ -327,19 +327,20 @@
"name": "DynRDSPSStyle",
"description": "PS Style Text (8 chars per update)",
"tip": "Sent 8 characters at a time. Program Service is the most commonly displayed part of RDS.",
"restart": 1,
"restart": 0,
"reboot": 0,
"type": "text",
"size": 32,
"maxlength": 64,
"default": "{T}|{A}[|{P} of {C}]|Merry|Christ-| -mas!"
"default": "{T}|{A}[|{P} of {C}]|Merry|Christ-| -mas!",
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>"
},
"DynRDSPSUpdateRate": {
"name": "DynRDSPSUpdateRate",
"description": "PS Update Rate",
"tip": "Interval between updating the 8 characters being sent. It takes ~1 second to send the 8 characters and some radios only display the text after receiving the full group twice.",
"suffix": "seconds",
"restart": 1,
"suffix": "seconds <i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>",
"restart": 0,
"reboot": 0,
"type": "number",
"min": 3,
Expand All @@ -351,19 +352,20 @@
"name": "DynRDSRTStyle",
"description": "RT Style Text",
"tip": "Recommended to send 32 characters at a time, but can send up to 64 characters. Radio Text is intended for longer message with a slower update rate.",
"restart": 1,
"restart": 0,
"reboot": 0,
"type": "text",
"size": 64,
"maxlength": 256,
"default": "{T}[ by {A}][|Track {P} of {C}] Merry Christmas!"
"default": "{T}[ by {A}][|Track {P} of {C}] Merry Christmas!",
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>"
},
"DynRDSRTUpdateRate": {
"name": "DynRDSRTUpdateRate",
"description": "RT Update Rate",
"tip": "Interval between updating the 32-64 characters being sent. It takes ~4 seconds to send the 64 characters and some radios only display the text after receiving the full group twice.",
"suffix": "seconds",
"restart": 1,
"suffix": "seconds <i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>",
"restart": 0,
"reboot": 0,
"type": "number",
"min": 3,
Expand Down Expand Up @@ -476,7 +478,7 @@
"checkedValue": "1",
"uncheckedValue": "0",
"default": 0,
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1'></i>"
"suffix": "<i class='fas fa-fw fa-bolt fa-nbsp ui-level-1' title='Takes effect immediately - no FPP restart'></i>"
},
"DynRDSmqttEnable": {
"name": "DynRDSmqttEnable",
Expand Down