@@ -2237,12 +2237,30 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
22372237 }
22382238
22392239 // v3.004+ non-body circuits: Use indexed Action 184 (Wireless-style)
2240- // Protocol: channel=0x688F, byte[2]=circuitId-1, target=0xA8ED, byte[6]=state
2241- // This is the native control method observed from the Wireless remote.
22422240 if ( sys . equipment . isIntellicenterV3 ) {
22432241 const circuit = sys . circuits . getItemById ( id , false ) ;
22442242 logger . info ( `v3.004+ setCircuitStateAsync: Using indexed Action 184 for circuit ${ id } (${ circuit ?. name || 'unnamed' } ) -> ${ val ? 'ON' : 'OFF' } ` ) ;
2245- let out = this . createAction184IndexedCircuitMessage ( id , val ) ;
2243+ /**
2244+ * v3.004+ Indexed Circuit Control (Wireless-style).
2245+ * Action 184 is the native circuit control message used by the Wireless remote.
2246+ *
2247+ * Payload structure (10 bytes):
2248+ * Bytes 0-1: Channel (0x688F for circuits)
2249+ * Byte 2: Index (circuitId - 1 or featureId - 1)
2250+ * Byte 3: Format (255 = command mode)
2251+ * Bytes 4-5: Target (0xA8ED = control primitive)
2252+ * Byte 6: State (0=OFF, 1=ON)
2253+ * Bytes 7-9: Reserved (0,0,0)
2254+ */
2255+ const idx = Math . max ( 0 , Math . min ( 255 , ( id | 0 ) - 1 ) ) ;
2256+ const out = Outbound . createMessage ( 184 , [
2257+ 104 , 143 , // Channel 0x688F (circuits)
2258+ idx , // Index (circuitId - 1)
2259+ 255 , // Format (command)
2260+ 168 , 237 , // Target 0xA8ED (control primitive)
2261+ val ? 1 : 0 , // State
2262+ 0 , 0 , 0
2263+ ] , 3 ) ;
22462264 out . dest = 16 ; // Send to OCP
22472265 out . scope = `circuitState${ id } ` ;
22482266 out . retries = 5 ;
@@ -2633,33 +2651,6 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
26332651 }
26342652 return out ;
26352653 }
2636- /**
2637- * v3.004+ Indexed Circuit Control (Wireless-style).
2638- * Action 184 is the native circuit control message used by the Wireless remote.
2639- *
2640- * Payload structure (10 bytes):
2641- * Bytes 0-1: Channel (0x688F for circuits, 0xE89D for features)
2642- * Byte 2: Index (circuitId - 1 or featureId - 1)
2643- * Byte 3: Format (255 = command mode)
2644- * Bytes 4-5: Target (0xA8ED = control primitive)
2645- * Byte 6: State (0=OFF, 1=ON)
2646- * Bytes 7-9: Reserved (0,0,0)
2647- *
2648- * @param circuitId The circuit ID (1-40)
2649- * @param isOn True to turn circuit ON, false for OFF
2650- * @returns Outbound message ready to send
2651- */
2652- public createAction184IndexedCircuitMessage ( circuitId : number , isOn : boolean ) : Outbound {
2653- const idx = Math . max ( 0 , Math . min ( 255 , ( circuitId | 0 ) - 1 ) ) ;
2654- return Outbound . createMessage ( 184 , [
2655- 104 , 143 , // Channel 0x688F (circuits)
2656- idx , // Index (circuitId - 1)
2657- 255 , // Format (command)
2658- 168 , 237 , // Target 0xA8ED (control primitive)
2659- isOn ? 1 : 0 , // State
2660- 0 , 0 , 0
2661- ] , 3 ) ;
2662- }
26632654
26642655 public async setDimmerLevelAsync ( id : number , level : number ) : Promise < ICircuitState > {
26652656 let circuit = sys . circuits . getItemById ( id ) ;
@@ -2692,8 +2683,73 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
26922683}
26932684class IntelliCenterFeatureCommands extends FeatureCommands {
26942685 declare board : IntelliCenterBoard ;
2695- public async setFeatureStateAsync ( id , val ) : Promise < ICircuitState > { return sys . board . circuits . setCircuitStateAsync ( id , val ) ; }
2696- public async toggleFeatureStateAsync ( id ) : Promise < ICircuitState > { return sys . board . circuits . toggleCircuitStateAsync ( id ) ; }
2686+
2687+ private async getConfigAsync ( payload : number [ ] ) : Promise < boolean > {
2688+ const dest = sys . equipment . isIntellicenterV3 ? 16 : 15 ;
2689+ let out = Outbound . create ( {
2690+ dest,
2691+ action : 222 ,
2692+ retries : 3 ,
2693+ payload : payload ,
2694+ response : Response . create ( { dest : - 1 , action : 30 , payload : payload } )
2695+ } ) ;
2696+ await out . sendAsync ( ) ;
2697+ // Do NOT ACK(30). Wireless captures show config succeeds without ACK(30), and v1 queue avoids ACK(30).
2698+ return true ;
2699+ }
2700+
2701+ public async setFeatureStateAsync ( id : number , val : boolean ) : Promise < ICircuitState > {
2702+ // v3.004+: Features are controlled via Action 184 channel 0xE89D (232,157), not the circuits channel.
2703+ if ( sys . equipment . isIntellicenterV3 ) {
2704+ if ( isNaN ( id ) ) return Promise . reject ( new InvalidEquipmentIdError ( `Invalid feature id: ${ id } ` , id , 'Feature' ) ) ;
2705+ if ( ! sys . board . equipmentIds . features . isInRange ( id ) ) return Promise . reject ( new InvalidEquipmentIdError ( `Invalid feature id: ${ id } ` , id , 'Feature' ) ) ;
2706+
2707+ const feature = sys . features . getItemById ( id , false , { isActive : false } ) ;
2708+ logger . info ( `v3.004+ setFeatureStateAsync: Using indexed Action 184 for feature ${ id } (${ feature ?. name || 'unnamed' } ) -> ${ val ? 'ON' : 'OFF' } ` ) ;
2709+
2710+ /**
2711+ * v3.004+ Indexed Feature Control (Wireless-style).
2712+ * Action 184 is the native feature control message used by the Wireless remote (channel 0xE89D).
2713+ *
2714+ * Payload structure (10 bytes):
2715+ * Bytes 0-1: Channel (0xE89D for features)
2716+ * Byte 2: Index (featureId - 1)
2717+ * Byte 3: Format/mode (observed 0 in replays 132/138 feature toggles)
2718+ * Bytes 4-5: Target (0xA8ED = control primitive)
2719+ * Byte 6: State (0=OFF, 1=ON)
2720+ * Bytes 7-9: Reserved (0,0,0)
2721+ */
2722+ const idx = Math . max ( 0 , Math . min ( 255 , ( id | 0 ) - 1 ) ) ;
2723+ const out = Outbound . createMessage ( 184 , [
2724+ 232 , 157 , // Channel 0xE89D (features)
2725+ idx , // Index (featureId - 1)
2726+ 0 , // Format/mode (observed)
2727+ 168 , 237 , // Target 0xA8ED (control primitive)
2728+ val ? 1 : 0 , // State
2729+ 0 , 0 , 0
2730+ ] , 3 ) ;
2731+ out . dest = 16 ; // Send to OCP
2732+ out . scope = `featureState${ id } ` ;
2733+ out . retries = 5 ;
2734+ out . response = IntelliCenterBoard . getAckResponse ( 184 ) ;
2735+ await out . sendAsync ( ) ;
2736+
2737+ // Request updated system state to confirm feature change (authoritative source for v3 features).
2738+ await this . getConfigAsync ( [ 15 , 0 ] ) ;
2739+
2740+ const fstate = state . features . getItemById ( id , true ) ;
2741+ state . emitEquipmentChanges ( ) ;
2742+ return fstate ;
2743+ }
2744+
2745+ // Legacy behavior (v1.x): delegate to circuit state setter.
2746+ return sys . board . circuits . setCircuitStateAsync ( id , val ) ;
2747+ }
2748+
2749+ public async toggleFeatureStateAsync ( id : number ) : Promise < ICircuitState > {
2750+ const feat = state . features . getItemById ( id ) ;
2751+ return this . setFeatureStateAsync ( id , ! ( feat . isOn || false ) ) ;
2752+ }
26972753 public syncGroupStates ( ) { } // Do nothing and let IntelliCenter do it.
26982754 public async setFeatureAsync ( data : any ) : Promise < Feature > {
26992755
0 commit comments