Pause Blue Iris Cameras from HA using Node-Red (Useful to get other camera attributes as well)

I wanted to be able to pause any Blue Iris camera if there was a lot of activity going on such as mowing the yard, etc. that I didn’t care about so that I didn’t get 100’s of alerts and video clips. The REST api calls to Blue Iris to pause/unpause the cameras was straight forward, but getting the the state for “isPaused” from Blue Iris for any of the cameras was a different story with the use of Session ID’s and Response ID’s needed to make the REST calls using JSON.

HA Cameras
ha-cameras

I used REST api calls to pause/unpause the cameras. (This is the straight forward part) I stored the REST URL’s in my secrets.yaml since they contain usernames and passwords

rest.yaml

  frontporch_pause:
    url: !secret frontporch_pause
    method: GET

  frontporch_unpause:
    url: !secret frontporch_unpause
    method: GET

secrets.yaml

frontporch_pause: 'http://192.168.1.13:81/admin?camera=fps&pause=-1&user=xxx&pw=xxx'
frontporch_unpause: 'http://192.168.1.13:81/admin?camera=fps&pause=0&user=xxx&pw=xxx'

I then created a dummy input_boolean (bi_pause_check) that is turned on as part of the camera pause and un-pause scripts to trigger the node-red flow upon state change. This will then update the cameras input_boolean (bi_fpsstate) with the paused state of the camera.

input_boolean.yaml

  bi_pause_check:
    name: BI Pause Check
    initial: off

  bi_fpsstate:
    name: Front Porch Paused
    initial: off

scripts.yaml

frontporch_pause_on:
  sequence:
    - service: rest_command.frontporch_pause
    - service: homeassistant.turn_on
      entity_id: input_boolean.bi_pause_check

frontporch_pause_off:
  sequence:
    - service: rest_command.frontporch_unpause
    - service: homeassistant.turn_on
      entity_id: input_boolean.bi_pause_check

The node-red flow below is triggered by the input_boolean.bi_pause_check and is also triggered every 5 mins to check state so that if someone manually pauses/unpauses the camera from Blue Iris itself that change would get reflected in HA.

I had to install the Credentials node which is used to hold your username and password that you setup for API access to Blue Iris so you may have to do the same. The credentials node does not export your username and password if you want to share your flow.

Blue Iris Node Red Flow

[{"id":"4fd4d833.c97508","type":"tab","label":"Blue Iris","disabled":false,"info":""},{"id":"d651c3f5.ae4ce","type":"server-state-changed","z":"4fd4d833.c97508","name":"Is BI Pause Check: ON","server":"5d9ca5ff.74e9ec","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_boolean.bi_pause_check","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"x":160,"y":160,"wires":[["bb29efe3.a5a2","3b6fe560.56770a"],[]]},{"id":"f681ecbd.5e22a","type":"http request","z":"4fd4d833.c97508","name":"Get BI Session ID","method":"POST","ret":"obj","paytoqs":false,"url":"http://192.168.1.13:81/json","tls":"","persist":true,"proxy":"","authType":"","x":650,"y":160,"wires":[["d112bc38.556c9"]]},{"id":"bb29efe3.a5a2","type":"template","z":"4fd4d833.c97508","name":"Set Login Cmd","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"cmd\":\"login\"}","output":"json","x":420,"y":160,"wires":[["f681ecbd.5e22a"]]},{"id":"2c5d8e3f.2afc22","type":"inject","z":"4fd4d833.c97508","name":"Get State Every 5 Mins","topic":"","payload":"","payloadType":"date","repeat":"300","crontab":"","once":true,"onceDelay":0.1,"x":150,"y":100,"wires":[["bb29efe3.a5a2"]]},{"id":"298c5810.8022b8","type":"function","z":"4fd4d833.c97508","name":"Set MD5 Hash Paylod","func":"p=msg.payload\nflow.set('bisession',p.session);\nmsg.payload = msg.username + \":\" + p.session + \":\" + msg.password;\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":220,"wires":[["7af00351.80c76c"]]},{"id":"7af00351.80c76c","type":"exec","z":"4fd4d833.c97508","command":"echo -n ","addpay":true,"append":"| md5sum","useSpawn":"false","timer":"","oldrc":false,"name":"MD5 Hash Session","x":690,"y":220,"wires":[["a6f296e2.a3e078"],[],[]]},{"id":"a6f296e2.a3e078","type":"function","z":"4fd4d833.c97508","name":"Set BI Login Payload","func":"var sesid = flow.get('bisession') || 0;\nvar str = msg.payload;\nvar resid = str.substring(0, str.length-4);\nflow.set('biresponse', resid);\nmsg.payload = '{\"cmd\":\"login\",\"session\":\"' + sesid + '\",\"response\":\"' + resid + '\"}';\nreturn msg;","outputs":1,"noerr":0,"x":280,"y":280,"wires":[["9ec0f71a.561448"]]},{"id":"f111cd24.3dfbd","type":"http request","z":"4fd4d833.c97508","name":"Login to BI","method":"POST","ret":"obj","paytoqs":false,"url":"http://192.168.1.13:81/json","tls":"","persist":true,"proxy":"","authType":"","x":750,"y":280,"wires":[["d05b73a8.446b8"]]},{"id":"9ec0f71a.561448","type":"json","z":"4fd4d833.c97508","name":"Convert to JSON","property":"payload","action":"","pretty":true,"x":530,"y":280,"wires":[["f111cd24.3dfbd"]]},{"id":"d05b73a8.446b8","type":"function","z":"4fd4d833.c97508","name":"Set BI CamList Payload","func":"var sesid = flow.get('bisession') || 0;\nvar resid = flow.get('biresponse') || 0;\nmsg.payload = '{\"cmd\":\"camlist\",\"session\":\"' + sesid + '\",\"response\":\"' + resid + '\"}';\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":340,"wires":[["5f32582a.a139d8"]]},{"id":"5f32582a.a139d8","type":"json","z":"4fd4d833.c97508","name":"Convert to JSON","property":"payload","action":"","pretty":true,"x":610,"y":340,"wires":[["a197a493.b413b8"]]},{"id":"a197a493.b413b8","type":"http request","z":"4fd4d833.c97508","name":"Get Cam Status","method":"POST","ret":"obj","paytoqs":false,"url":"http://192.168.1.13:81/json","tls":"","persist":true,"proxy":"","authType":"","x":840,"y":340,"wires":[["c8e8816e.02b15"]]},{"id":"40d5458c.c74fec","type":"json","z":"4fd4d833.c97508","name":"Convert to JSON","property":"payload","action":"","pretty":true,"x":850,"y":400,"wires":[["cc39595f.d995a8","2acb8b86.a37ce4","6a2b3cb2.62ba24","dce1d75d.bb3a28"]]},{"id":"cc39595f.d995a8","type":"function","z":"4fd4d833.c97508","name":"Set Front Porch State","func":"p=msg.payload;\nvar fpsstate = p.fpsstate;\nif ( fpsstate == 'true' ) {\n  msg.payload = 'on';\n}\nelse {\n  msg.payload = 'off'\n}\nreturn msg;","outputs":1,"noerr":0,"x":1112,"y":310,"wires":[["c37962ee.847fe"]]},{"id":"2acb8b86.a37ce4","type":"function","z":"4fd4d833.c97508","name":"Set Driveway State","func":"p=msg.payload;\nvar dsstate = p.dsstate;\nif ( dsstate == 'true' ) {\n  msg.payload = 'on';\n}\nelse {\n  msg.payload = 'off'\n}\nreturn msg;","outputs":1,"noerr":0,"x":1102,"y":370,"wires":[["6d8463b3.32f27c"]]},{"id":"6a2b3cb2.62ba24","type":"function","z":"4fd4d833.c97508","name":"Set Garage State","func":"p=msg.payload;\nvar gsstate = p.gsstate;\nif ( gsstate == 'true' ) {\n  msg.payload = 'on';\n}\nelse {\n  msg.payload = 'off'\n}\nreturn msg;","outputs":1,"noerr":0,"x":1102,"y":430,"wires":[["cdc20e9f.9d77a"]]},{"id":"dce1d75d.bb3a28","type":"function","z":"4fd4d833.c97508","name":"Set Backyard State","func":"p=msg.payload;\nvar bsstate = p.bsstate;\nif ( bsstate == 'true' ) {\n  msg.payload = 'on';\n}\nelse {\n  msg.payload = 'off'\n}\nreturn msg;","outputs":1,"noerr":0,"x":1102,"y":490,"wires":[["1d6feacd.ec2b55"]]},{"id":"3b6fe560.56770a","type":"api-call-service","z":"4fd4d833.c97508","name":"Set BI Pause Check: OFF","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_off","entityId":"input_boolean.bi_pause_check","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":450,"y":100,"wires":[[]]},{"id":"c37962ee.847fe","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Front Porch Paused State","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_{{payload}}","entityId":"input_boolean.bi_fpsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1382,"y":310,"wires":[[]]},{"id":"6d8463b3.32f27c","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Driveway Paused State","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_{{payload}}","entityId":"input_boolean.bi_dsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1372,"y":370,"wires":[[]]},{"id":"cdc20e9f.9d77a","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Garage Paused State","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_{{payload}}","entityId":"input_boolean.bi_gsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1362,"y":430,"wires":[[]]},{"id":"1d6feacd.ec2b55","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Backyard Paused State","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_{{payload}}","entityId":"input_boolean.bi_bsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1372,"y":490,"wires":[[]]},{"id":"758a6dbe.8bbae4","type":"comment","z":"4fd4d833.c97508","name":"Get BI Camera Pause States","info":"","x":140,"y":40,"wires":[]},{"id":"505fe94b.551ec8","type":"bigtimer","z":"4fd4d833.c97508","outtopic":"","outpayload1":"1","outpayload2":"","name":"Is it 10:00pm?","comment":"","lat":"38.429722","lon":"-90.5744","starttime":"1320","endtime":"10001","starttime2":0,"endtime2":0,"startoff":"0","endoff":0,"startoff2":0,"endoff2":0,"offs":0,"outtext1":"","outtext2":"","timeout":1440,"sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"day1":0,"month1":0,"day2":0,"month2":0,"day3":0,"month3":0,"day4":0,"month4":0,"day5":0,"month5":0,"day6":0,"month6":0,"day7":"","month7":"","day8":"","month8":"","day9":"","month9":"","day10":"","month10":"","day11":"","month11":"","day12":"","month12":"","d1":0,"w1":0,"d2":0,"w2":0,"d3":0,"w3":0,"d4":0,"w4":0,"d5":0,"w5":0,"d6":0,"w6":0,"xday1":0,"xmonth1":0,"xday2":0,"xmonth2":0,"xday3":0,"xmonth3":0,"xday4":0,"xmonth4":0,"xday5":0,"xmonth5":0,"xday6":0,"xmonth6":0,"xd1":0,"xw1":0,"xd2":0,"xw2":0,"xd3":0,"xw3":0,"xd4":0,"xw4":0,"xd5":0,"xw5":0,"xd6":0,"xw6":0,"suspend":false,"random":false,"repeat":false,"atstart":false,"odd":false,"even":false,"x":100,"y":600,"wires":[["3daa6bbc.2a7744","f6998dcf.b63b1","f6332b98.556c38","757da0a0.0e47d"],[],[]]},{"id":"3daa6bbc.2a7744","type":"api-current-state","z":"4fd4d833.c97508","name":"Front Porch Paused: YES","server":"5d9ca5ff.74e9ec","version":1,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_boolean.bi_fpsstate","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":390,"y":510,"wires":[["2ec6bf7f.3799b"],[]]},{"id":"f6998dcf.b63b1","type":"api-current-state","z":"4fd4d833.c97508","name":"Driveway Paused: YES","server":"5d9ca5ff.74e9ec","version":1,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_boolean.bi_dsstate","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":390,"y":570,"wires":[["1cb4d5c1.ea8a9a"],[]]},{"id":"f6332b98.556c38","type":"api-current-state","z":"4fd4d833.c97508","name":"Garage Paused: YES","server":"5d9ca5ff.74e9ec","version":1,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_boolean.bi_gsstate","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":380,"y":630,"wires":[["bd98c1ae.9c99f"],[]]},{"id":"757da0a0.0e47d","type":"api-current-state","z":"4fd4d833.c97508","name":"Backyard Paused: YES","server":"5d9ca5ff.74e9ec","version":1,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_boolean.bi_bsstate","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":390,"y":690,"wires":[["607daf83.4358f"],[]]},{"id":"2ec6bf7f.3799b","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Front Porch Pause: OFF","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_off","entityId":"input_boolean.bi_fpsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":720,"y":510,"wires":[[]]},{"id":"1cb4d5c1.ea8a9a","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Driveway Pause: OFF","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_off","entityId":"input_boolean.bi_dsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":710,"y":570,"wires":[[]]},{"id":"bd98c1ae.9c99f","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Garage Pause: OFF","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_off","entityId":"input_boolean.bi_gsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":710,"y":630,"wires":[[]]},{"id":"607daf83.4358f","type":"api-call-service","z":"4fd4d833.c97508","name":"Set Backyard Pause: OFF","server":"5d9ca5ff.74e9ec","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_off","entityId":"input_boolean.bi_bsstate","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":710,"y":690,"wires":[[]]},{"id":"b7c3f80b.3b948","type":"inject","z":"4fd4d833.c97508","name":"","topic":"","payload":"1","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":530,"wires":[["3daa6bbc.2a7744","f6998dcf.b63b1","f6332b98.556c38","757da0a0.0e47d"]]},{"id":"c8e8816e.02b15","type":"function","z":"4fd4d833.c97508","name":"Set BI CamList Payload","func":"p=msg.payload;\nvar fpsstate = '';\nvar dsstate = '';\nvar gsstate = '';\nvar bsstate = '';\n\nfor (i = 1; i < p.data.length; i=i+1) {\n    if (p.data[i].optionValue == 'fps') {\n        fpsstate = p.data[i].isPaused\n    }\n    if (p.data[i].optionValue == 'ds') {\n        dsstate = p.data[i].isPaused\n    }\n    if (p.data[i].optionValue == 'gs') {\n        gsstate = p.data[i].isPaused\n    }\n    if (p.data[i].optionValue == 'bs') {\n        bsstate = p.data[i].isPaused\n    }\n}\n\n//Reset Flow Variables\nflow.set('bisession','0');\nflow.set('biresponse', '0');\nmsg.payload = '{\"fpsstate\":\"' + fpsstate + '\",\"dsstate\":\"' + dsstate + '\",\"gsstate\":\"' + gsstate + '\",\"bsstate\":\"' + bsstate + '\"}';\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":400,"wires":[["40d5458c.c74fec"]]},{"id":"d112bc38.556c9","type":"credentials","z":"4fd4d833.c97508","name":"Bi Credentials","props":[{"value":"username","type":"msg"},{"value":"password","type":"msg"}],"x":200,"y":220,"wires":[["298c5810.8022b8"]]},{"id":"de38d094.87056","type":"comment","z":"4fd4d833.c97508","name":"Un-Pause All Cameras at 10:00PM","info":"","x":160,"y":460,"wires":[]},{"id":"5d9ca5ff.74e9ec","type":"server","z":"","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

In the “Set BI CamList Payload” node, you will see I’m parsing the “isPaused” value for 4 cameras. I loop through all the cameras and parse for the short names of the cameras I am interested in as I have 11 camera streams and don’t care about the others. The array pulled in will change based on if a camera is enable/disabled and other factors so looping through the data and looking at “optionValue” which is the short name of the camera ensures I pull the state for the correct camera.

The EXEC Node (The one with the gear icon) sends a command to the CLI to generate the md5hash of username:sessionid:password in order to get the Blue Iris Response ID required to make more authenticated calls to the API.

My flow also has a timer that will unpause all cameras at 10:00PM in case I forget to unpause them manually.

I also used custom-button-card on my tablet view page so the icon and label changes with state…I’m sure you can use other cards, but this was my preference.

ui-lovelace.yaml

          ### Front Porch
          - type: horizontal-stack
            cards:
              - type: 'custom:button-card'
                entity: camera.front_porch
                class: camera
                show_name: true
                show_icon: false
                show_entity_picture: true
                styles:
                  card:
                    - height: 76px
              - type: 'custom:button-card'
                entity: script.frontporch_pause_on
                show_name: false
                show_label: true
                icon: >
                  [[[
                    if (states['input_boolean.bi_fpsstate'].state == 'off')
                      return "mdi:pause-circle";
                    else
                      return "mdi:play";
                  ]]]
                label: >
                  [[[
                    if (states['input_boolean.bi_fpsstate'].state == 'off')
                      return "Pause";
                    else
                      return "Resume";
                  ]]]
                styles:
                  card:
                    - height: 76px
                tap_action:
                  action: call-service
                  service: input_boolean.toggle
                  service_data:
                    entity_id: input_boolean.bi_fpsstate

Hope this helps someone looking to do similar things. There are many camera attributes pulled in from the camlist API call that I’m sure could be used in other ways as well.

1 Like

Just wanted to say nice job, buddy.

1 Like

That’s indeed impressive, the question is why not using the profile? Much simpler and can be done with single call

I’m not sure what “profile” you are referring to. :frowning:

If you are using BI server, you can set the different profiles to do predefined actions as pause, record, control triggers and msny other actions

Ahh Gotcha, this was just a personal exercise in using the API and all the functionality you can access with it. Authentication was the hardest part to get working.

Why not just use a simple mqtt message?!

I ended up using mqtt as I could never get rest or curl methods to update the cameras.
I use mqtt in nodered - add some switches in HA and use those to trigger the mqtt commands like turning on a motion sensor