2019-09-09 10:48:09 -07:00
const data = require ( './stub/data' ) ;
const logger = require ( './stub/logger' ) ;
const zigbeeHerdsman = require ( './stub/zigbeeHerdsman' ) ;
const MQTT = require ( './stub/mqtt' ) ;
2019-03-29 11:10:13 -07:00
const settings = require ( '../lib/util/settings' ) ;
2019-09-09 10:48:09 -07:00
const Controller = require ( '../lib/controller' ) ;
const flushPromises = ( ) => new Promise ( setImmediate ) ;
2020-09-24 09:06:43 -07:00
const stringify = require ( 'json-stable-stringify-without-jsonify' ) ;
2021-01-22 10:56:44 -07:00
jest . mock ( 'debounce' , ( ) => jest . fn ( fn => fn ) ) ;
const debounce = require ( 'debounce' ) ;
2019-09-09 10:48:09 -07:00
2020-04-15 11:36:40 -07:00
describe ( 'Bind' , ( ) => {
2019-09-09 10:48:09 -07:00
let controller ;
mockClear = ( device ) => {
for ( const endpoint of device . endpoints ) {
endpoint . read . mockClear ( ) ;
endpoint . write . mockClear ( ) ;
endpoint . configureReporting . mockClear ( ) ;
2020-07-12 12:56:11 -07:00
endpoint . bind = jest . fn ( ) ;
2019-09-09 10:48:09 -07:00
endpoint . bind . mockClear ( ) ;
2020-01-27 12:56:11 -07:00
endpoint . unbind . mockClear ( ) ;
2019-03-15 13:19:42 -07:00
}
2019-09-09 10:48:09 -07:00
}
beforeEach ( async ( ) => {
data . writeDefaultConfiguration ( ) ;
2021-03-09 11:50:05 -07:00
settings . reRead ( ) ;
2019-09-09 10:48:09 -07:00
data . writeEmptyState ( ) ;
2021-01-22 10:56:44 -07:00
zigbeeHerdsman . groups . group _1 . members = [ ] ;
zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) . configureReporting . mockClear ( ) ;
zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) . bind . mockClear ( ) ;
zigbeeHerdsman . devices . bulb _color _2 . getEndpoint ( 1 ) . read . mockClear ( ) ;
debounce . mockClear ( ) ;
2021-02-06 08:32:20 -07:00
controller = new Controller ( jest . fn ( ) , jest . fn ( ) ) ;
2019-09-09 10:48:09 -07:00
await controller . start ( ) ;
await flushPromises ( ) ;
this . coordinatorEndoint = zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) ;
MQTT . publish . mockClear ( ) ;
2019-03-15 13:19:42 -07:00
} ) ;
2021-01-22 10:56:44 -07:00
it ( 'Should bind to device and configure reporting' , async ( ) => {
2020-07-12 12:56:11 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
2021-01-22 10:56:44 -07:00
// Setup
const originalDeviceOutputClusters = device . getEndpoint ( 1 ) . outputClusters ;
device . getEndpoint ( 1 ) . outputClusters = [ ... device . getEndpoint ( 1 ) . outputClusters , 768 ] ;
const originalTargetBinds = target . binds ;
target . binds = [ { cluster : { name : 'genLevelCtrl' } , target : zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) } ] ;
target . getClusterAttributeValue . mockImplementationOnce ( ( cluster , value ) => undefined ) ;
2020-07-12 12:56:11 -07:00
mockClear ( device ) ;
2021-01-22 10:56:44 -07:00
target . configureReporting . mockImplementationOnce ( ( ) => { throw new Error ( "timeout" ) } ) ;
2021-03-24 11:17:49 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { transaction : "1234" , from : 'remote' , to : 'bulb_color' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
2021-01-22 10:56:44 -07:00
expect ( target . read ) . toHaveBeenCalledWith ( 'lightingColorCtrl' , [ 'colorCapabilities' ] ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 4 ) ;
2020-07-12 12:56:11 -07:00
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
2021-01-22 10:56:44 -07:00
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "lightingColorCtrl" , target ) ;
expect ( target . configureReporting ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( target . configureReporting ) . toHaveBeenCalledWith ( "genOnOff" , [ { "attribute" : "onOff" , "maximumReportInterval" : 3600 , "minimumReportInterval" : 0 , "reportableChange" : 0 } ] ) ;
expect ( target . configureReporting ) . toHaveBeenCalledWith ( "genLevelCtrl" , [ { "attribute" : "currentLevel" , "maximumReportInterval" : 3600 , "minimumReportInterval" : 5 , "reportableChange" : 1 } ] ) ;
expect ( target . configureReporting ) . toHaveBeenCalledWith ( "lightingColorCtrl" , [ { "attribute" : "colorTemperature" , "minimumReportInterval" : 5 , "maximumReportInterval" : 3600 , "reportableChange" : 1 } , { "attribute" : "currentX" , "minimumReportInterval" : 5 , "maximumReportInterval" : 3600 , "reportableChange" : 1 } , { "attribute" : "currentY" , "minimumReportInterval" : 5 , "maximumReportInterval" : 3600 , "reportableChange" : 1 } ] ) ;
2020-07-12 12:56:11 -07:00
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2021-03-24 11:17:49 -07:00
stringify ( { "transaction" : "1234" , "data" : { "from" : "remote" , "to" : "bulb_color" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" , "lightingColorCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
2020-09-04 09:42:24 -07:00
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/devices' ,
expect . any ( String ) ,
{ retain : true , qos : 0 } ,
expect . any ( Function )
) ;
2021-01-22 10:56:44 -07:00
// Teardown
target . binds = originalTargetBinds ;
device . getEndpoint ( 1 ) . outputClusters = originalDeviceOutputClusters ;
2020-07-12 12:56:11 -07:00
} ) ;
2020-07-28 12:05:02 -07:00
it ( 'Should bind only specifief clusters' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote' , to : 'bulb_color' , clusters : [ "genOnOff" ] } ) ) ;
2020-07-28 12:05:02 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "bulb_color" , "clusters" : [ "genOnOff" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-28 12:05:02 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
2020-07-12 12:56:11 -07:00
it ( 'Should log error when there is nothing to bind' , async ( ) => {
const device = zigbeeHerdsman . devices . bulb _color ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
logger . error . mockClear ( ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote' , to : 'button' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 0 ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "button" , "clusters" : [ ] , "failed" : [ ] } , "status" : "error" , "error" : "Nothing to bind" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Should unbind' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
2021-01-22 10:56:44 -07:00
// setup
target . configureReporting . mockImplementationOnce ( ( ) => { throw new Error ( "timeout" ) } ) ;
const originalRemoteBinds = device . getEndpoint ( 1 ) . binds ;
device . getEndpoint ( 1 ) . binds = [ ] ;
const originalTargetBinds = target . binds ;
target . binds = [
{ cluster : { name : 'genOnOff' } , target : zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) } ,
{ cluster : { name : 'genLevelCtrl' } , target : zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) } ,
{ cluster : { name : 'lightingColorCtrl' } , target : zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) }
] ;
2020-07-12 12:56:11 -07:00
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
2021-01-22 10:56:44 -07:00
delete zigbeeHerdsman . devices . bulb _color . meta . configured ;
expect ( zigbeeHerdsman . devices . bulb _color . meta . configured ) . toBe ( undefined ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/unbind' , stringify ( { from : 'remote' , to : 'bulb_color' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
2021-01-22 10:56:44 -07:00
// Disable reporting
expect ( target . configureReporting ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( target . configureReporting ) . toHaveBeenCalledWith ( "genOnOff" , [ { "attribute" : "onOff" , "maximumReportInterval" : 0xFFFF , "minimumReportInterval" : 0 , "reportableChange" : 0 } ] ) ;
expect ( target . configureReporting ) . toHaveBeenCalledWith ( "genLevelCtrl" , [ { "attribute" : "currentLevel" , "maximumReportInterval" : 0xFFFF , "minimumReportInterval" : 5 , "reportableChange" : 1 } ] ) ;
expect ( target . configureReporting ) . toHaveBeenCalledWith ( "lightingColorCtrl" , [ { "attribute" : "colorTemperature" , "minimumReportInterval" : 5 , "maximumReportInterval" : 0xFFFF , "reportableChange" : 1 } , { "attribute" : "currentX" , "minimumReportInterval" : 5 , "maximumReportInterval" : 0xFFFF , "reportableChange" : 1 } , { "attribute" : "currentY" , "minimumReportInterval" : 5 , "maximumReportInterval" : 0xFFFF , "reportableChange" : 1 } ] ) ;
expect ( zigbeeHerdsman . devices . bulb _color . meta . configured ) . toBe ( 2 ) ;
2020-07-12 12:56:11 -07:00
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/unbind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "bulb_color" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
2021-01-22 10:56:44 -07:00
// Teardown
target . binds = originalTargetBinds ;
device . getEndpoint ( 1 ) . binds = originalRemoteBinds ;
2020-07-12 12:56:11 -07:00
} ) ;
it ( 'Should unbind coordinator' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
endpoint . unbind . mockClear ( ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/unbind' , stringify ( { from : 'remote' , to : 'Coordinator' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/unbind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "Coordinator" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Should bind to groups' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . groups . group _1 ;
2021-01-22 10:56:44 -07:00
const target1Member = zigbeeHerdsman . devices . bulb . getEndpoint ( 1 ) ;
2020-07-12 12:56:11 -07:00
const endpoint = device . getEndpoint ( 1 ) ;
2021-01-22 10:56:44 -07:00
target . members . push ( target1Member ) ;
target1Member . configureReporting . mockClear ( ) ;
2020-07-12 12:56:11 -07:00
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote' , to : 'group_1' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
2021-01-22 10:56:44 -07:00
expect ( target1Member . configureReporting ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( target1Member . configureReporting ) . toHaveBeenCalledWith ( "genOnOff" , [ { "attribute" : "onOff" , "maximumReportInterval" : 3600 , "minimumReportInterval" : 0 , "reportableChange" : 0 } ] ) ;
expect ( target1Member . configureReporting ) . toHaveBeenCalledWith ( "genLevelCtrl" , [ { "attribute" : "currentLevel" , "maximumReportInterval" : 3600 , "minimumReportInterval" : 5 , "reportableChange" : 1 } ] ) ;
2020-07-12 12:56:11 -07:00
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "group_1" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
2021-01-22 10:56:44 -07:00
// Should configure reproting for device added to group
target1Member . configureReporting . mockClear ( ) ;
await MQTT . events . message ( 'zigbee2mqtt/bridge/group/group_1/add' , 'bulb' ) ;
await flushPromises ( ) ;
expect ( target1Member . configureReporting ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( target1Member . configureReporting ) . toHaveBeenCalledWith ( "genOnOff" , [ { "attribute" : "onOff" , "maximumReportInterval" : 3600 , "minimumReportInterval" : 0 , "reportableChange" : 0 } ] ) ;
expect ( target1Member . configureReporting ) . toHaveBeenCalledWith ( "genLevelCtrl" , [ { "attribute" : "currentLevel" , "maximumReportInterval" : 3600 , "minimumReportInterval" : 5 , "reportableChange" : 1 } ] ) ;
} ) ;
it ( 'Should unbind from group' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . groups . group _1 ;
const target1Member = zigbeeHerdsman . devices . bulb . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
target . members . push ( target1Member ) ;
target1Member . configureReporting . mockClear ( ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/unbind' , stringify ( { from : 'remote' , to : 'group_1' } ) ) ;
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/unbind' ,
stringify ( { "data" : { "from" : "remote" , "to" : "group_1" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
2020-07-12 12:56:11 -07:00
} ) ;
it ( 'Should bind to group by number' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . groups . group _1 ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote' , to : '1' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "1" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Should log when bind fails' , async ( ) => {
logger . error . mockClear ( ) ;
const device = zigbeeHerdsman . devices . remote ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
endpoint . bind . mockImplementation ( ( ) => { throw new Error ( 'failed' ) } ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote' , to : 'bulb_color' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "bulb_color" , "clusters" : [ ] , "failed" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] } , "status" : "error" , "error" : "Failed to bind" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Should bind from non default endpoints' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . QBKG03LM . getEndpoint ( 3 ) ;
const endpoint = device . getEndpoint ( 2 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote/ep2' , to : 'wall_switch_double/right' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote/ep2" , "to" : "wall_switch_double/right" , "clusters" : [ "genOnOff" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Should bind to default endpoint returned by endpoints()' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . QBKG04LM . getEndpoint ( 2 ) ;
const endpoint = device . getEndpoint ( 2 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote/ep2' , to : 'wall_switch' } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote/ep2" , "to" : "wall_switch" , "clusters" : [ "genOnOff" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Should unbind from default_bind_group' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = 'default_bind_group' ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/unbind' , stringify ( { from : 'remote' , to : target } ) ) ;
2020-07-12 12:56:11 -07:00
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , 901 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , 901 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , 901 ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/unbind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "default_bind_group" , "clusters" : [ "genScenes" , "genOnOff" , "genLevelCtrl" ] , "failed" : [ ] } , "status" : "ok" } ) ,
2020-07-12 12:56:11 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
2020-07-12 14:50:15 -07:00
it ( 'Error bind fails when source not existing' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote_not_existing' , to : 'bulb_color' } ) ) ;
2020-07-12 14:50:15 -07:00
await flushPromises ( ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote_not_existing" , "to" : "bulb_color" } , "status" : "error" , "error" : "Source device 'remote_not_existing' does not exist" } ) ,
2020-07-12 14:50:15 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
it ( 'Error bind fails when target not existing' , async ( ) => {
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
2020-08-13 11:00:35 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/request/device/bind' , stringify ( { from : 'remote' , to : 'bulb_color_not_existing' } ) ) ;
2020-07-12 14:50:15 -07:00
await flushPromises ( ) ;
expect ( MQTT . publish ) . toHaveBeenCalledWith (
'zigbee2mqtt/bridge/response/device/bind' ,
2020-08-13 11:00:35 -07:00
stringify ( { "data" : { "from" : "remote" , "to" : "bulb_color_not_existing" } , "status" : "error" , "error" : "Target device or group 'bulb_color_not_existing' does not exist" } ) ,
2020-07-12 14:50:15 -07:00
{ retain : false , qos : 0 } , expect . any ( Function )
) ;
} ) ;
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should bind' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote' , 'bulb_color' ) ;
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish . mock . calls [ 0 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 0 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'bulb_color' , cluster : 'genScenes' } } ) ;
expect ( MQTT . publish . mock . calls [ 1 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 1 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'bulb_color' , cluster : 'genOnOff' } } ) ;
expect ( MQTT . publish . mock . calls [ 2 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 2 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'bulb_color' , cluster : 'genLevelCtrl' } } ) ;
2019-03-15 13:19:42 -07:00
} ) ;
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should log error when there is nothing to bind' , async ( ) => {
2020-03-20 11:00:00 -07:00
const device = zigbeeHerdsman . devices . bulb _color ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
logger . error . mockClear ( ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote' , 'button' ) ;
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 0 ) ;
expect ( logger . error ) . toHaveBeenCalledWith ( ` Nothing to bind from 'remote' to 'button' ` ) ;
} ) ;
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should unbind' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/unbind/remote' , 'bulb_color' ) ;
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish . mock . calls [ 0 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 0 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'bulb_color' , cluster : 'genScenes' } } ) ;
expect ( MQTT . publish . mock . calls [ 1 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 1 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'bulb_color' , cluster : 'genOnOff' } } ) ;
expect ( MQTT . publish . mock . calls [ 2 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 2 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'bulb_color' , cluster : 'genLevelCtrl' } } ) ;
} ) ;
2019-03-29 11:10:13 -07:00
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should unbind coordinator' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . coordinator . getEndpoint ( 1 ) ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
endpoint . unbind . mockClear ( ) ;
2019-10-09 10:40:46 -07:00
MQTT . events . message ( 'zigbee2mqtt/bridge/unbind/remote' , 'Coordinator' ) ;
2019-09-09 10:48:09 -07:00
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish . mock . calls [ 0 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 0 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'Coordinator' , cluster : 'genScenes' } } ) ;
expect ( MQTT . publish . mock . calls [ 1 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 1 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'Coordinator' , cluster : 'genOnOff' } } ) ;
expect ( MQTT . publish . mock . calls [ 2 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 2 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'Coordinator' , cluster : 'genLevelCtrl' } } ) ;
} ) ;
2019-03-29 11:10:13 -07:00
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should bind to groups' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . groups . group _1 ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote' , 'group_1' ) ;
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish . mock . calls [ 0 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 0 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'group_1' , cluster : 'genScenes' } } ) ;
expect ( MQTT . publish . mock . calls [ 1 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 1 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'group_1' , cluster : 'genOnOff' } } ) ;
expect ( MQTT . publish . mock . calls [ 2 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 2 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'group_1' , cluster : 'genLevelCtrl' } } ) ;
} ) ;
2019-03-15 13:19:42 -07:00
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should bind to group by number' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . groups . group _1 ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote' , '1' ) ;
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genLevelCtrl" , target ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genScenes" , target ) ;
expect ( MQTT . publish . mock . calls [ 0 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 0 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'group_1' , cluster : 'genScenes' } } ) ;
expect ( MQTT . publish . mock . calls [ 1 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 1 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'group_1' , cluster : 'genOnOff' } } ) ;
expect ( MQTT . publish . mock . calls [ 2 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 2 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_bind' , message : { from : 'remote' , to : 'group_1' , cluster : 'genLevelCtrl' } } ) ;
} ) ;
2019-03-15 13:19:42 -07:00
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should log when bind fails' , async ( ) => {
2019-09-09 10:48:09 -07:00
logger . error . mockClear ( ) ;
const device = zigbeeHerdsman . devices . remote ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
endpoint . bind . mockImplementationOnce ( ( ) => { throw new Error ( 'failed' ) } ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote' , 'bulb_color' ) ;
await flushPromises ( ) ;
expect ( logger . error ) . toHaveBeenCalledWith ( "Failed to bind cluster 'genScenes' from 'remote' to 'bulb_color' (Error: failed)" ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 3 ) ;
} ) ;
2019-06-25 10:13:59 -07:00
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should bind from non default endpoints' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . QBKG03LM . getEndpoint ( 3 ) ;
const endpoint = device . getEndpoint ( 2 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote/ep2' , 'wall_switch_double/right' ) ;
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
2019-03-15 13:19:42 -07:00
} ) ;
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should bind to default endpoint returned by endpoints()' , async ( ) => {
2019-09-09 10:48:09 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = zigbeeHerdsman . devices . QBKG04LM . getEndpoint ( 2 ) ;
const endpoint = device . getEndpoint ( 2 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/bind/remote/ep2' , 'wall_switch' ) ;
await flushPromises ( ) ;
expect ( endpoint . bind ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( endpoint . bind ) . toHaveBeenCalledWith ( "genOnOff" , target ) ;
2019-03-15 13:19:42 -07:00
} ) ;
2020-01-27 12:56:11 -07:00
2020-07-12 12:56:11 -07:00
it ( 'Legacy api: Should unbind from default_bind_group' , async ( ) => {
2020-01-27 12:56:11 -07:00
const device = zigbeeHerdsman . devices . remote ;
const target = 'default_bind_group' ;
const endpoint = device . getEndpoint ( 1 ) ;
mockClear ( device ) ;
MQTT . events . message ( 'zigbee2mqtt/bridge/unbind/remote' , target ) ;
await flushPromises ( ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genOnOff" , 901 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genLevelCtrl" , 901 ) ;
expect ( endpoint . unbind ) . toHaveBeenCalledWith ( "genScenes" , 901 ) ;
expect ( MQTT . publish . mock . calls [ 0 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 0 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'default_bind_group' , cluster : 'genScenes' } } ) ;
expect ( MQTT . publish . mock . calls [ 1 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 1 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'default_bind_group' , cluster : 'genOnOff' } } ) ;
expect ( MQTT . publish . mock . calls [ 2 ] [ 0 ] ) . toStrictEqual ( 'zigbee2mqtt/bridge/log' ) ;
expect ( JSON . parse ( MQTT . publish . mock . calls [ 2 ] [ 1 ] ) ) . toStrictEqual ( { type : 'device_unbind' , message : { from : 'remote' , to : 'default_bind_group' , cluster : 'genLevelCtrl' } } ) ;
} ) ;
2021-01-22 10:56:44 -07:00
it ( 'Should poll bounded Hue bulb when receiving message from Hue dimmer' , async ( ) => {
const remote = zigbeeHerdsman . devices . remote ;
const data = { "button" : 3 , "unknown1" : 3145728 , "type" : 2 , "unknown2" : 0 , "time" : 1 } ;
const payload = { data , cluster : 'manuSpecificPhilips' , device : remote , endpoint : remote . getEndpoint ( 2 ) , type : 'commandHueNotification' , linkquality : 10 , groupID : 0 } ;
await zigbeeHerdsman . events . message ( payload ) ;
await flushPromises ( ) ;
expect ( debounce ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( zigbeeHerdsman . devices . bulb _color . getEndpoint ( 1 ) . read ) . toHaveBeenCalledWith ( "genLevelCtrl" , [ "currentLevel" ] ) ;
} ) ;
it ( 'Should poll grouped Hue bulb when receiving message from TRADFRI remote' , async ( ) => {
zigbeeHerdsman . devices . bulb _color _2 . getEndpoint ( 1 ) . read . mockClear ( ) ;
zigbeeHerdsman . devices . bulb _2 . getEndpoint ( 1 ) . read . mockClear ( ) ;
const remote = zigbeeHerdsman . devices . tradfri _remote ;
const data = { "stepmode" : 0 , "stepsize" : 43 , "transtime" : 5 } ;
const payload = { data , cluster : 'genLevelCtrl' , device : remote , endpoint : remote . getEndpoint ( 1 ) , type : 'commandStepWithOnOff' , linkquality : 10 , groupID : 15071 } ;
await zigbeeHerdsman . events . message ( payload ) ;
await flushPromises ( ) ;
2021-02-09 09:20:33 -07:00
expect ( debounce ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( zigbeeHerdsman . devices . bulb _color _2 . getEndpoint ( 1 ) . read ) . toHaveBeenCalledTimes ( 2 ) ;
2021-01-22 10:56:44 -07:00
expect ( zigbeeHerdsman . devices . bulb _color _2 . getEndpoint ( 1 ) . read ) . toHaveBeenCalledWith ( "genLevelCtrl" , [ "currentLevel" ] ) ;
2021-02-09 09:20:33 -07:00
expect ( zigbeeHerdsman . devices . bulb _color _2 . getEndpoint ( 1 ) . read ) . toHaveBeenCalledWith ( "genOnOff" , [ "onOff" ] ) ;
2021-01-22 10:56:44 -07:00
// Should also only debounce once
await zigbeeHerdsman . events . message ( payload ) ;
await flushPromises ( ) ;
2021-02-09 09:20:33 -07:00
expect ( debounce ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( zigbeeHerdsman . devices . bulb _color _2 . getEndpoint ( 1 ) . read ) . toHaveBeenCalledTimes ( 4 ) ;
2021-01-22 10:56:44 -07:00
// Should only call Hue bulb, not e.g. tradfri
expect ( zigbeeHerdsman . devices . bulb _2 . getEndpoint ( 1 ) . read ) . toHaveBeenCalledTimes ( 0 ) ;
} ) ;
2019-03-15 13:19:42 -07:00
} ) ;