Skip to content

Commit

Permalink
- Fixed a critical event routing bug
Browse files Browse the repository at this point in the history
- Fixed several action init problems
  • Loading branch information
mthiel committed Mar 2, 2025
1 parent 8d9d769 commit 95b79be
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 85 deletions.
20 changes: 16 additions & 4 deletions src/actions/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ export class PluginAction extends SingletonAction {
}
}

// Set up or refresh the listener for receiver events
if (this.manifestId) {
this.avrConnections[receiverId].on(this.routeReceiverEvent.bind(this), this.manifestId);
}

// Update the map with the selected receiver ID for this action
this.actionReceiverMap[ev.action.id] = {
uuid: receiverId,
Expand Down Expand Up @@ -156,6 +161,11 @@ export class PluginAction extends SingletonAction {
zone: zone
};
statusMsg = connection.status.statusMsg;

// Set up or refresh the listener for receiver events
if (this.manifestId) {
this.avrConnections[settings.uuid].on(this.routeReceiverEvent.bind(this), this.manifestId);
}
} else {
// If we failed to connect, clear the association
delete this.actionReceiverMap[ev.action.id];
Expand All @@ -168,6 +178,11 @@ export class PluginAction extends SingletonAction {
zone: zone
};
statusMsg = this.avrConnections[settings.uuid].status.statusMsg;

// Set up or refresh the listener for receiver events
if (this.manifestId) {
this.avrConnections[settings.uuid].on(this.routeReceiverEvent.bind(this), this.manifestId);
}
}
} else {
// Ensure the receiver association is cleared if no receiver is selected
Expand Down Expand Up @@ -236,9 +251,6 @@ export class PluginAction extends SingletonAction {
this.logger.info(`Creating new receiver connection to ${receiverInfo.name || receiverInfo.currentIP}.`);
const connection = new AVRConnection(this.plugin, receiverId, receiverInfo.currentIP);
this.avrConnections[receiverId] = connection;

// Set up listener for receiver events
this.avrConnections[receiverId].on(this.routeReceiverEvent.bind(this));
}

return this.avrConnections[receiverId];
Expand Down Expand Up @@ -360,4 +372,4 @@ export class PluginAction extends SingletonAction {
* @param {ReceiverEvent} ev - The event object.
*/
onReceiverMuteChanged(ev) {}
}
}
34 changes: 12 additions & 22 deletions src/actions/dynVol.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import streamDeck, { action } from "@elgato/streamdeck";
/** @typedef {import("@elgato/streamdeck").KeyDownEvent} KeyDownEvent */
/** @typedef {import("@elgato/streamdeck").WillAppearEvent} WillAppearEvent */
/** @typedef {import("@elgato/streamdeck").KeyAction} KeyAction */
/** @typedef {import("@elgato/streamdeck").Action} Action */
/** @typedef {import("@elgato/streamdeck").SendToPluginEvent} SendToPluginEvent */

import { PluginAction } from "./action";
Expand All @@ -27,12 +27,9 @@ export class DynVolAction extends PluginAction {
async onWillAppear(ev) {
await super.onWillAppear(ev);

// If there's no connection yet, there's nothing to do
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
if (!connection) return;

// Set the initial state of the action based on the receiver's dynamic volume status
if (ev.action.isKey()) updateActionStatus(ev.action, connection);
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
updateActionStatus(ev.action, connection);
}

/**
Expand Down Expand Up @@ -61,10 +58,8 @@ export class DynVolAction extends PluginAction {
async onUserChoseReceiver(ev) {
await super.onUserChoseReceiver(ev);

const connection = this.avrConnections[this.actionReceiverMap[ev.action.id].uuid];
if (!connection) return;

if (ev.action.isKey()) updateActionStatus(ev.action, connection);
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
updateActionStatus(ev.action, connection);
}

/**
Expand All @@ -74,27 +69,22 @@ export class DynVolAction extends PluginAction {
onReceiverDynamicVolumeChanged(ev) {
if (!ev.actions) return;

Promise.all(
ev.actions.map(async (action) => {
// Filter any non-key actions
if (action.isKey() === false) return;

updateActionStatus(action, ev.connection);
})
);
Promise.all(ev.actions.map(async (action) => updateActionStatus(action, ev.connection)));
}
}

/**
* Update the state of an action based on the receiver's dynamic volume status.
* @param {KeyAction} action - The action object.
* @param {AVRConnection} connection - The receiver connection object.
* @param {Action} action - The action object.
* @param {AVRConnection} [connection] - The receiver connection object.
*/
async function updateActionStatus(action, connection) {
/** @type {DynamicVolume} */
const dynamicVolume = connection.status.zones[0].dynamicVolume;
const dynamicVolume = connection !== undefined ? connection.status.zones[0].dynamicVolume : undefined;

if (action.isKey() === false) return;

if (!dynamicVolume) {
if (dynamicVolume === undefined) {
action.setState(0); // Unknown state
return;
}
Expand Down
36 changes: 21 additions & 15 deletions src/actions/power.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ export class PowerAction extends PluginAction {
async onWillAppear(ev) {
await super.onWillAppear(ev);

// If there's no connection yet, there's nothing to do
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
if (!connection) return;

// Set the initial state of the action based on the receiver's power status
if (ev.action.isKey()) {
const zone = /** @type {number} */ (ev.payload.settings.zone) || 0;
ev.action.setState(connection.status.zones[zone].power ? 0 : 1);
}
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
const zone = /** @type {number} */ (ev.payload.settings.zone) || 0;
updateActionState(ev.action, connection, zone);
}

/**
Expand Down Expand Up @@ -61,13 +56,24 @@ export class PowerAction extends PluginAction {
onReceiverPowerChanged(ev) {
if (!ev.actions) return;

Promise.all(
ev.actions.map(async (action) => {
// Filter any non-key actions that don't match the event zone
if (action.isKey() === false) return;
Promise.all(ev.actions.map(async (action) => updateActionState(action, ev.connection, ev.zone)));
}
}

/**
* Update the state of an action based on the receiver's power status.
* @param {Action} action - The action object.
* @param {AVRConnection} [connection] - The receiver connection object.
* @param {number} [zone] - The zone that the power status changed for
*/
async function updateActionState(action, connection, zone) {
const actionZone = (/** @type {ActionSettings} */ (await action.getSettings())).zone || 0;
if (zone !== undefined && zone !== actionZone) { return; }

const { power } = connection !== undefined ? connection.status.zones[actionZone] : {};

action.setState(ev.connection.status.zones[ev.zone || 0].power ? 0 : 1);
})
);
const state = power !== undefined ? power ? 0 : 1 : 1;
if (action.isKey()) {
action.setState(state);
}
}
28 changes: 1 addition & 27 deletions src/actions/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ import { AVRConnection } from "../modules/connection";
*/
@action({ UUID: "com.mthiel.denon-controller.source" })
export class SourceAction extends PluginAction {
async onWillAppear(ev) {
await super.onWillAppear(ev);

// Set the initial state of the action
// const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]];
// if (connection) {
// updateActionState(ev.action, connection);
// }
}

/**
* Change to the configured source when the key is pressed
* @param {KeyDownEvent} ev - The event object.
Expand Down Expand Up @@ -69,9 +59,6 @@ export class SourceAction extends PluginAction {
* @param {SendToPluginEvent} ev - The event object.
*/
async onRefreshSourceListForPI(ev) {
// const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]];
// if (!connection) return;

/** @type {ActionSettings} */
const settings = await ev.action.getSettings();
const zone = /** @type {number} */ (settings.zone) || 0;
Expand Down Expand Up @@ -108,17 +95,4 @@ export class SourceAction extends PluginAction {
});

}
}

/**
* Update the state of the action
* @param {Action} action - The action object.
* @param {AVRConnection} connection - The receiver connection object.
* @param {number} [zone=0]
*/
async function updateActionState(action, connection, zone = 0) {
const actionZone = parseInt("" + (await action.getSettings()).zone) || 0;
if (action.isKey() && actionZone === zone) {
// action.setState(connection.status.zones[zone].power ? 0 : 1);
}
}
}
30 changes: 18 additions & 12 deletions src/actions/volume.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ export class VolumeAction extends PluginAction {
async onWillAppear(ev) {
await super.onWillAppear(ev);

// If there's no connection yet, there's nothing to do
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
if (!connection) return;

// Set the initial state of the action based on the receiver's volume & mute status
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
updateActionState(ev.action, connection);
}

Expand Down Expand Up @@ -117,7 +114,8 @@ export class VolumeAction extends PluginAction {
await super.onUserChoseReceiver(ev);

// Update the action state for the new receiver
updateActionState(ev.action, this.avrConnections[this.actionReceiverMap[ev.action.id].uuid]);
const connection = this.avrConnections[this.actionReceiverMap[ev.action.id]?.uuid];
updateActionState(ev.action, connection);
}

/**
Expand Down Expand Up @@ -154,28 +152,36 @@ export class VolumeAction extends PluginAction {
/**
* Update the state of an action based on the receiver's volume & mute status.
* @param {Action} action - The action object.
* @param {AVRConnection} connection - The receiver connection object.
* @param {AVRConnection} [connection] - The receiver connection object.
* @param {number} [zone] - The zone that the volume status changed for
*/
async function updateActionState(action, connection, zone) {
const actionZone = (/** @type {ActionSettings} */ (await action.getSettings())).zone || 0;
if (zone !== undefined && zone !== actionZone) { return; }

const { muted, volume, maxVolume, power } = connection.status.zones[actionZone];
const { muted, volume, maxVolume, power } = connection !== undefined
? connection.status.zones[actionZone] : {};

if (action.isDial()) {
const indicatorValue = volume !== undefined && maxVolume !== undefined
? muted || !power ? 0 : (volume / maxVolume) * 100 : undefined;

const value = power !== undefined ? !power ? "Off" : muted ? "Muted" : `Vol: ${volume}` : "";

const icon = power !== undefined ? muted || !power ? images.muted : images.unmuted : images.muted;

action.setFeedback({
indicator: {
value: muted || !power ? 0 : (volume / maxVolume) * 100
value: indicatorValue
},
value: !power ? "Off" :
muted ? "Muted" : `Vol: ${volume}`
value: value
});

action.setFeedback({
icon: muted || !power ? images.muted : images.unmuted
icon: icon
});
} else if (action.isKey()) {
action.setState(muted || !power ? 1 : 0);
const state = power !== undefined ? muted || !power ? 1 : 0 : 1;
action.setState(state);
}
}
24 changes: 19 additions & 5 deletions src/modules/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ export class AVRConnection {
*/
#eventEmitter = new EventEmitter();

/**
* The listeners for this instance
* @type {string[]}
*/
#listenerIds = [];

/**
* The raw socket connection to the receiver
* @type {net.Socket | undefined}
Expand Down Expand Up @@ -200,6 +206,9 @@ export class AVRConnection {
let rawSocket = this.#rawSocket;
let telnet = this.#telnet;

// Clear the listeners for this instance
this.#listenerIds = [];

// Dispose of this instance's sockets
this.#rawSocket = undefined;
this.#telnet = undefined;
Expand Down Expand Up @@ -367,13 +376,18 @@ export class AVRConnection {
/**
* Subscribe to events from this receiver
* @param {EventListener} listener - The listener function to call when the event is emitted
* @param {string} id - The binding ID for this listener, should be the manifest ID of the action that is listening
*/
on(listener) {
on(listener, id) {
const listenerId = `${id}-${listener.name}`;

// Don't add the same listener twice
if (this.#eventEmitter.listeners("event").includes(listener)) {
if (this.#listenerIds.includes(listenerId)) {
return;
}

this.#listenerIds.push(listenerId);

this.#eventEmitter.on("event", listener);
}

Expand Down Expand Up @@ -512,9 +526,9 @@ export class AVRConnection {
this.emit("powerChanged", zone);

// Request the full status of the receiver if it is powered on
if (status.power) {
this.#requestFullReceiverStatus();
}
// if (status.power) {
// this.#requestFullReceiverStatus();
// }
}

/**
Expand Down

0 comments on commit 95b79be

Please sign in to comment.