@@ -27,6 +27,23 @@ const checkOption = (device: Zh.Device | DummyDevice, options: KeyValue, key: st
2727 return defaultValue ;
2828} ;
2929
30+ const shellySwitchInputEndpoints = ( device : Zh . Device | DummyDevice , endpoints : Record < string , number > ) : Record < string , number > => {
31+ if ( utils . isDummyDevice ( device ) ) return endpoints ;
32+
33+ return Object . fromEntries ( Object . entries ( endpoints ) . filter ( ( [ , endpoint ] ) => device . getEndpoint ( endpoint ) !== undefined ) ) ;
34+ } ;
35+
36+ const shellySwitchInputExposes = ( device : Zh . Device | DummyDevice , endpoints : Record < string , number > ) : Expose [ ] =>
37+ Object . keys ( shellySwitchInputEndpoints ( device , endpoints ) ) . map ( ( endpoint ) =>
38+ e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( endpoint ) ,
39+ ) ;
40+
41+ const shellyDeviceEndpoints = ( endpoints : Record < string , number > ) : ModernExtend => ( {
42+ meta : { multiEndpoint : true } ,
43+ endpoint : ( device ) => shellySwitchInputEndpoints ( device , endpoints ) ,
44+ isModernExtend : true ,
45+ } ) ;
46+
3047const shellyPresenceEndpointNames = ( device : Zh . Device | DummyDevice ) : string [ ] => {
3148 const count =
3249 ! utils . isDummyDevice ( device ) && typeof device . meta . presence_zone_count === "number"
@@ -520,6 +537,13 @@ const shellyModernExtend = {
520537 return JSON . parse ( accumulated . substring ( 0 , expectedLen ) ) ;
521538 } ;
522539
540+ const getRPCEndpoint = ( entity : Zh . Endpoint | Zh . Group ) : Zh . Endpoint | Zh . Group => {
541+ utils . assertEndpoint ( entity ) ;
542+ const endpoint = entity . getDevice ( ) . getEndpoint ( SHELLY_ENDPOINT_ID ) ;
543+ if ( ! endpoint ) throw new Error ( `Shelly RPC endpoint ${ SHELLY_ENDPOINT_ID } not found` ) ;
544+ return endpoint ;
545+ } ;
546+
523547 const rpcReceive = async ( endpoint : Zh . Endpoint | Zh . Group , key : string ) => {
524548 logger . debug ( `||| shellyRPC rpcReceive(${ key } )` , NS ) ;
525549 if ( key === "rpc_rxctl" ) {
@@ -533,6 +557,19 @@ const shellyModernExtend = {
533557 }
534558 } ;
535559
560+ const getShellySwitchConfig = async ( endpoint : Zh . Endpoint | Zh . Group , id : number ) : Promise < KeyValue | undefined > => {
561+ const response = await rpcRequest ( endpoint , "Switch.GetConfig" , { id} ) ;
562+ const result = response ?. result ?? response ?. params ?? response ;
563+ if ( ! result ) return undefined ;
564+ assertObject < KeyValue > ( result ) ;
565+ return result ;
566+ } ;
567+
568+ const getShellySwitchMode = async ( endpoint : Zh . Endpoint | Zh . Group , id : number ) : Promise < string | undefined > => {
569+ const config = await getShellySwitchConfig ( endpoint , id ) ;
570+ return typeof config ?. in_mode === "string" ? config . in_mode : undefined ;
571+ } ;
572+
536573 // Features for exposes
537574 const featurePercentage = ( name : string , label : string ) => {
538575 return e . numeric ( name , ea . STATE_SET ) . withValueMin ( 0 ) . withValueMax ( 100 ) . withValueStep ( 1 ) . withLabel ( label ) . withUnit ( "%" ) ;
@@ -790,33 +827,30 @@ const shellyModernExtend = {
790827 const inModeValues = [ "follow" , "flip" , "detached" , "cycle" , "activation" ] ;
791828 exposes . push ( ( device : Zh . Device | DummyDevice , _options : KeyValue ) => {
792829 if ( utils . isDummyDevice ( device ) || ! device . getEndpoint ( SHELLY_ENDPOINT_ID ) ) return [ ] ;
793- return [
794- e . enum ( "switch_mode" , ea . ALL , inModeValues ) . withDescription ( "Switch input mode" ) . withCategory ( "config" ) . withEndpoint ( "sw1" ) ,
795- e . enum ( "switch_mode" , ea . ALL , inModeValues ) . withDescription ( "Switch input mode" ) . withCategory ( "config" ) . withEndpoint ( "sw2" ) ,
796- ] ;
830+ return Object . keys ( shellySwitchInputEndpoints ( device , { sw1 : 2 , sw2 : 3 } ) ) . map ( ( endpoint ) =>
831+ e . enum ( "switch_mode" , ea . ALL , inModeValues ) . withDescription ( "Switch input mode" ) . withCategory ( "config" ) . withEndpoint ( endpoint ) ,
832+ ) ;
797833 } ) ;
798834 toZigbee . push ( {
799835 key : [ "switch_mode" ] ,
800836 convertSet : async ( entity , key , value , meta ) => {
801837 const switchId = meta . endpoint_name === "sw1" ? 0 : 1 ;
802- const ep = determineEndpoint ( entity , meta , "shellyRPCCluster" ) ;
838+ const ep = getRPCEndpoint ( entity ) ;
803839 await rpcSend ( ep , "Switch.SetConfig" , { id : switchId , config : { in_mode : value } } ) ;
804840 return { state : { switch_mode : value } } ;
805841 } ,
806842 convertGet : async ( entity , key , meta ) => {
807843 const switchId = meta . endpoint_name === "sw1" ? 0 : 1 ;
808- const ep = determineEndpoint ( entity , meta , "shellyRPCCluster" ) ;
809- await rpcSend ( ep , "Switch.GetConfig" , { id : switchId } ) ;
810- await rpcReceive ( ep , "rpc_rxctl" ) ;
844+ const ep = getRPCEndpoint ( entity ) ;
845+ await getShellySwitchMode ( ep , switchId ) ;
811846 } ,
812847 } ) ;
813848 configure . push ( async ( device ) => {
814849 const ep = device . getEndpoint ( SHELLY_ENDPOINT_ID ) ;
815850 if ( ! ep ) return ;
816851 try {
817852 for ( const id of [ 0 , 1 ] ) {
818- await rpcSend ( ep , "Switch.GetConfig" , { id} ) ;
819- await rpcReceive ( ep , "rpc_rxctl" ) ;
853+ await getShellySwitchConfig ( ep , id ) ;
820854 }
821855 } catch ( e ) {
822856 logger . warning ( `Failed to read switch_mode during configure, use get to retry: ${ e } ` , NS ) ;
@@ -827,27 +861,27 @@ const shellyModernExtend = {
827861 const inModeValues = [ "follow" , "flip" , "detached" , "cycle" , "activation" ] ;
828862 exposes . push ( ( device : Zh . Device | DummyDevice , _options : KeyValue ) => {
829863 if ( utils . isDummyDevice ( device ) || ! device . getEndpoint ( SHELLY_ENDPOINT_ID ) ) return [ ] ;
830- return [ e . enum ( "switch_mode" , ea . ALL , inModeValues ) . withDescription ( "Switch input mode" ) . withCategory ( "config" ) . withEndpoint ( "sw1" ) ] ;
864+ return Object . keys ( shellySwitchInputEndpoints ( device , { sw1 : 2 } ) ) . map ( ( endpoint ) =>
865+ e . enum ( "switch_mode" , ea . ALL , inModeValues ) . withDescription ( "Switch input mode" ) . withCategory ( "config" ) . withEndpoint ( endpoint ) ,
866+ ) ;
831867 } ) ;
832868 toZigbee . push ( {
833869 key : [ "switch_mode" ] ,
834870 convertSet : async ( entity , key , value , meta ) => {
835- const ep = determineEndpoint ( entity , meta , "shellyRPCCluster" ) ;
871+ const ep = getRPCEndpoint ( entity ) ;
836872 await rpcSend ( ep , "Switch.SetConfig" , { id : 0 , config : { in_mode : value } } ) ;
837873 return { state : { switch_mode : value } } ;
838874 } ,
839875 convertGet : async ( entity , key , meta ) => {
840- const ep = determineEndpoint ( entity , meta , "shellyRPCCluster" ) ;
841- await rpcSend ( ep , "Switch.GetConfig" , { id : 0 } ) ;
842- await rpcReceive ( ep , "rpc_rxctl" ) ;
876+ const ep = getRPCEndpoint ( entity ) ;
877+ await getShellySwitchMode ( ep , 0 ) ;
843878 } ,
844879 } ) ;
845880 configure . push ( async ( device ) => {
846881 const ep = device . getEndpoint ( SHELLY_ENDPOINT_ID ) ;
847882 if ( ! ep ) return ;
848883 try {
849- await rpcSend ( ep , "Switch.GetConfig" , { id : 0 } ) ;
850- await rpcReceive ( ep , "rpc_rxctl" ) ;
884+ await getShellySwitchConfig ( ep , 0 ) ;
851885 } catch ( e ) {
852886 logger . warning ( `Failed to read switch_mode during configure, use get to retry: ${ e } ` , NS ) ;
853887 }
@@ -1387,7 +1421,7 @@ const fzLocal = {
13871421 type : [ "attributeReport" , "readResponse" ] ,
13881422 convert : ( model , msg , publish , options , meta ) => {
13891423 if ( ! Object . hasOwn ( msg . data , "switchType" ) ) return { } ;
1390- const epName = utils . getFromLookup ( msg . endpoint . ID , { 2 : "sw1" , 3 : "sw1 " , 4 : "sw2" } ) ;
1424+ const epName = utils . getFromLookup ( msg . endpoint . ID , { 2 : "sw1" , 3 : "sw2 " , 4 : "sw2" } ) ;
13911425 if ( ! epName ) return { } ;
13921426 return { [ `switch_type_${ epName } ` ] : utils . getFromLookup ( msg . data . switchType as number , { 0 : "toggle" , 1 : "momentary" } ) } ;
13931427 } ,
@@ -1447,12 +1481,12 @@ export const definitions: DefinitionWithExtend[] = [
14471481 ota : true ,
14481482 fromZigbee : [ fzLocal . one_switch_input_events , fzLocal . one_switch_input_scene_events , fzLocal . switch_input_type ] ,
14491483 toZigbee : [ tzLocal . switch_input_type ] ,
1450- exposes : [
1484+ exposes : ( device ) => [
14511485 e . action ( [ "input_1_on" , "input_1_off" , "input_1_toggle" , "input_1_single" , "input_1_double" , "input_1_triple" , "input_1_hold" ] ) ,
1452- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( " sw1" ) ,
1486+ ... shellySwitchInputExposes ( device , { sw1 : 2 } ) ,
14531487 ] ,
14541488 extend : [
1455- m . deviceEndpoints ( { endpoints : { sw1 : 2 } } ) ,
1489+ shellyDeviceEndpoints ( { sw1 : 2 } ) ,
14561490 m . onOff ( { powerOnBehavior : false } ) ,
14571491 ...shellyModernExtend . shellyCustomClusters ( ) ,
14581492 shellyModernExtend . shellyRPCSetup ( [ "1PMInputMode" ] ) ,
@@ -1475,12 +1509,12 @@ export const definitions: DefinitionWithExtend[] = [
14751509 ota : true ,
14761510 fromZigbee : [ fzLocal . one_switch_input_events , fzLocal . one_switch_input_scene_events , fzLocal . switch_input_type ] ,
14771511 toZigbee : [ tzLocal . switch_input_type ] ,
1478- exposes : [
1512+ exposes : ( device ) => [
14791513 e . action ( [ "input_1_on" , "input_1_off" , "input_1_toggle" , "input_1_single" , "input_1_double" , "input_1_triple" , "input_1_hold" ] ) ,
1480- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( " sw1" ) ,
1514+ ... shellySwitchInputExposes ( device , { sw1 : 2 } ) ,
14811515 ] ,
14821516 extend : [
1483- m . deviceEndpoints ( { endpoints : { sw1 : 2 } } ) ,
1517+ shellyDeviceEndpoints ( { sw1 : 2 } ) ,
14841518 m . onOff ( { powerOnBehavior : false } ) ,
14851519 ...shellyModernExtend . shellyCustomClusters ( ) ,
14861520 shellyModernExtend . shellyRPCSetup ( [ "1PMInputMode" ] ) ,
@@ -1503,12 +1537,12 @@ export const definitions: DefinitionWithExtend[] = [
15031537 ota : true ,
15041538 fromZigbee : [ fzLocal . one_switch_input_events , fzLocal . one_switch_input_scene_events , fzLocal . switch_input_type ] ,
15051539 toZigbee : [ tzLocal . switch_input_type ] ,
1506- exposes : [
1540+ exposes : ( device ) => [
15071541 e . action ( [ "input_1_on" , "input_1_off" , "input_1_toggle" , "input_1_single" , "input_1_double" , "input_1_triple" , "input_1_hold" ] ) ,
1508- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( " sw1" ) ,
1542+ ... shellySwitchInputExposes ( device , { sw1 : 2 } ) ,
15091543 ] ,
15101544 extend : [
1511- m . deviceEndpoints ( { endpoints : { sw1 : 2 } } ) ,
1545+ shellyDeviceEndpoints ( { sw1 : 2 } ) ,
15121546 m . onOff ( { powerOnBehavior : false } ) ,
15131547 m . electricityMeter ( { producedEnergy : true , acFrequency : true } ) ,
15141548 shellyModernExtend . shellyPowerFactorInt16Fix ( ) ,
@@ -1533,12 +1567,12 @@ export const definitions: DefinitionWithExtend[] = [
15331567 ota : true ,
15341568 fromZigbee : [ fzLocal . one_switch_input_events , fzLocal . one_switch_input_scene_events , fzLocal . switch_input_type ] ,
15351569 toZigbee : [ tzLocal . switch_input_type ] ,
1536- exposes : [
1570+ exposes : ( device ) => [
15371571 e . action ( [ "input_1_on" , "input_1_off" , "input_1_toggle" , "input_1_single" , "input_1_double" , "input_1_triple" , "input_1_hold" ] ) ,
1538- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( " sw1" ) ,
1572+ ... shellySwitchInputExposes ( device , { sw1 : 2 } ) ,
15391573 ] ,
15401574 extend : [
1541- m . deviceEndpoints ( { endpoints : { sw1 : 2 } } ) ,
1575+ shellyDeviceEndpoints ( { sw1 : 2 } ) ,
15421576 m . onOff ( { powerOnBehavior : false } ) ,
15431577 m . electricityMeter ( { producedEnergy : true , acFrequency : true } ) ,
15441578 shellyModernExtend . shellyPowerFactorInt16Fix ( ) ,
@@ -1618,7 +1652,7 @@ export const definitions: DefinitionWithExtend[] = [
16181652 ota : true ,
16191653 fromZigbee : [ fzLocal . two_switch_inputs_events , fzLocal . two_switch_inputs_scene_events , fzLocal . switch_input_type ] ,
16201654 toZigbee : [ tzLocal . switch_input_type ] ,
1621- exposes : [
1655+ exposes : ( device ) => [
16221656 e . action ( [
16231657 "input_1_on" ,
16241658 "input_1_off" ,
@@ -1635,11 +1669,10 @@ export const definitions: DefinitionWithExtend[] = [
16351669 "input_2_triple" ,
16361670 "input_2_hold" ,
16371671 ] ) ,
1638- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( "sw1" ) ,
1639- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( "sw2" ) ,
1672+ ...shellySwitchInputExposes ( device , { sw1 : 2 , sw2 : 3 } ) ,
16401673 ] ,
16411674 extend : [
1642- m . deviceEndpoints ( { endpoints : { sw1 : 2 , sw2 : 3 } } ) ,
1675+ shellyDeviceEndpoints ( { sw1 : 2 , sw2 : 3 } ) ,
16431676 shellyModernExtend . shellyWindowCovering ( ) ,
16441677 ...shellyModernExtend . shellyCustomClusters ( ) ,
16451678 shellyModernExtend . shellyRPCSetup ( [ "2PMInputMode" , "CoverTiltAuto" ] ) ,
@@ -1690,7 +1723,7 @@ export const definitions: DefinitionWithExtend[] = [
16901723 ota : true ,
16911724 fromZigbee : [ fzLocal . two_switch_inputs_events , fzLocal . two_switch_inputs_scene_events , fzLocal . switch_input_type ] ,
16921725 toZigbee : [ tzLocal . switch_input_type ] ,
1693- exposes : [
1726+ exposes : ( device ) => [
16941727 e . action ( [
16951728 "input_1_on" ,
16961729 "input_1_off" ,
@@ -1707,11 +1740,10 @@ export const definitions: DefinitionWithExtend[] = [
17071740 "input_2_triple" ,
17081741 "input_2_hold" ,
17091742 ] ) ,
1710- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( "sw1" ) ,
1711- e . enum ( "switch_type" , ea . ALL , [ "toggle" , "momentary" ] ) . withDescription ( "Switch input type" ) . withCategory ( "config" ) . withEndpoint ( "sw2" ) ,
1743+ ...shellySwitchInputExposes ( device , { sw1 : 3 , sw2 : 4 } ) ,
17121744 ] ,
17131745 extend : [
1714- m . deviceEndpoints ( { endpoints : { l1 : 1 , l2 : 2 , sw1 : 3 , sw2 : 4 } } ) ,
1746+ shellyDeviceEndpoints ( { l1 : 1 , l2 : 2 , sw1 : 3 , sw2 : 4 } ) ,
17151747 m . onOff ( { powerOnBehavior : false , endpointNames : [ "l1" , "l2" ] } ) ,
17161748 m . electricityMeter ( { producedEnergy : true , acFrequency : true , endpointNames : [ "l1" , "l2" ] } ) ,
17171749 shellyModernExtend . shellyPowerFactorInt16Fix ( ) ,
0 commit comments