Controlling Syncthing rate limits using Home Assistant
Posted
Contents
Introduction
Syncthing is a fantastic open-source file synchronization application that I use to keep folders in sync between my home and remote servers. For larger transfers, Syncthing will max out my home internet connection, which can make streaming or browsing a bit of a pain.
While Syncthing does support rate limiting of transfers, it can be frustrating to run through the user authentication and change the limit manually every time. For my use-case, a simple toggle switch somewhere with a predefined rate-limit would be ideal.
Enter Home Assistant, my home automation and control platform of choice. I use my Home Assistant dashboard as a control panel for the whole house, and it is always easily accessible. While Home Assistant has a massive library of Integrations, there is not yet any official support for Syncthing.
In this guide we will be using Home Assistant and Node-RED (via the Community Add-On) to build a small control panel that lets us enable/disable rate limiting and monitor our current sync progress.
Syncthing Configuration
The first piece of information we will need is your API key, which can be found by opening your Syncthing settings panel.
Next, we will need the ID of the folder you would like to monitor. You can find this by clicking on the folder on the Syncthing homepage and noting down the “Folder ID” field.
Once you have noted these down, we can move on to configuring Node-RED.
Node-RED Flow
While Home Assistant has a REST Sensor that can be used to pull in values from a RESTful API, I was unable to find an easy way to modify the contents of a received payload and fire off a POST request to the same endpoint. Instead, we will be using Node-RED to achieve this using the flow below.
[{"id":"a37931fd.2c5168","type":"tab","label":"Syncthing Rate Limit","disabled":false,"info":""},{"id":"f0fb11fd.0cd388","type":"http request","z":"a37931fd.2c5168","name":"","method":"GET","ret":"obj","paytoqs":false,"url":"http://ServerIPAddress:8384/rest/system/config","tls":"","persist":false,"proxy":"","authType":"","x":1030,"y":260,"wires":[["72fd4fc8.b5c9f8"]]},{"id":"35c91ab0.3483e6","type":"function","z":"a37931fd.2c5168","name":"Set request authentication headers","func":"msg.headers = {};\nmsg.headers['X-API-Key'] = 'YourAPIKeyHere';\nreturn msg;","outputs":1,"noerr":0,"x":540,"y":360,"wires":[["c503bb57.9f42d"]]},{"id":"72fd4fc8.b5c9f8","type":"function","z":"a37931fd.2c5168","name":"Turn rate limit on","func":"msg.payload['options']['maxRecvKbps'] = 512;\nreturn msg;","outputs":1,"noerr":0,"x":1230,"y":260,"wires":[["fdcb51dd.e94768"]]},{"id":"c1e8b3e2.71f9f8","type":"server-state-changed","z":"a37931fd.2c5168","name":"On Rate Limit Switch Change","server":"dc61337d.58b1c","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"switch.syncthing_rate_limit","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":240,"y":360,"wires":[["35c91ab0.3483e6"]]},{"id":"c6ee1233.354d9","type":"ha-entity","z":"a37931fd.2c5168","name":"Rate Limit Switch","server":"dc61337d.58b1c","version":1,"debugenabled":false,"outputs":2,"entityType":"switch","config":[{"property":"name","value":"syncthing_rate_limit"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","x":210,"y":460,"wires":[[],[]]},{"id":"c503bb57.9f42d","type":"switch","z":"a37931fd.2c5168","name":"Switch by State","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":800,"y":360,"wires":[["f0fb11fd.0cd388"],["f30f2996.e375a"]]},{"id":"f30f2996.e375a","type":"http request","z":"a37931fd.2c5168","name":"","method":"GET","ret":"obj","paytoqs":false,"url":"http://ServerIPAddress:8384/rest/system/config","tls":"","persist":false,"proxy":"","authType":"","x":1030,"y":460,"wires":[["d79bfe81.456018"]]},{"id":"d79bfe81.456018","type":"function","z":"a37931fd.2c5168","name":"Turn rate limit off","func":"msg.payload['options']['maxRecvKbps'] = 0;\nreturn msg;","outputs":1,"noerr":0,"x":1230,"y":460,"wires":[["556c19fb.8af3"]]},{"id":"b4596b4a.7e44c","type":"http request","z":"a37931fd.2c5168","name":"","method":"POST","ret":"obj","paytoqs":false,"url":"http://ServerIPAddress:8384/rest/system/config","tls":"","persist":false,"proxy":"","authType":"","x":1750,"y":260,"wires":[[]]},{"id":"fdcb51dd.e94768","type":"function","z":"a37931fd.2c5168","name":"Set request authentication headers","func":"msg.headers = {};\nmsg.headers['X-API-Key'] = 'YourAPIKeyHere';\nreturn msg;","outputs":1,"noerr":0,"x":1500,"y":260,"wires":[["b4596b4a.7e44c"]]},{"id":"685f0344.65d7c4","type":"http request","z":"a37931fd.2c5168","name":"","method":"POST","ret":"obj","paytoqs":false,"url":"http://ServerIPAddress:8384/rest/system/config","tls":"","persist":false,"proxy":"","authType":"","x":1750,"y":460,"wires":[[]]},{"id":"556c19fb.8af3","type":"function","z":"a37931fd.2c5168","name":"Set request authentication headers","func":"msg.headers = {};\nmsg.headers['X-API-Key'] = 'YourAPIKeyHere';\nreturn msg;","outputs":1,"noerr":0,"x":1500,"y":460,"wires":[["685f0344.65d7c4"]]},{"id":"d65dcea4.71f8c","type":"comment","z":"a37931fd.2c5168","name":"On","info":"","x":850,"y":300,"wires":[]},{"id":"fc5a5c21.e5c078","type":"comment","z":"a37931fd.2c5168","name":"Off","info":"","x":850,"y":420,"wires":[]},{"id":"cd2f0b76.be8cb","type":"comment","z":"a37931fd.2c5168","name":"GET current configuration","info":"","x":1050,"y":360,"wires":[]},{"id":"dc9eb39f.49e198","type":"comment","z":"a37931fd.2c5168","name":"POST new configuration","info":"","x":1750,"y":360,"wires":[]},{"id":"dc61337d.58b1c","type":"server","z":"","name":"Home Assistant","addon":true}]
There are one or two things that will probably need to be edited in the above flow to work in your setup:
- Replace the API key in the
Set request authentication headers
nodes with the API key that you obtained earlier - Edit the
http request
nodes and replaceServerIPAddress
with the IP address of your server running Syncthing - Make sure that the Home Assistant nodes
On Rate Limit Switch Change
andRate Limit Switch
are configured using the correct Home Assistant server
You can also edit the Turn rate limit on
node to adjust the limit that will be applied when you turn toggle the switch. I currently have this set to 512 KiB/s.
This flow does the following:
- Exposes a new switch to Home Assistant named
syncthing_rate_limit
- Watches for state changes on
syncthing_rate_limit
- When the state is changed:
- Makes a GET request with authentication headers to the
/rest/system/config
endpoint on your Syncthing server - Edits the
options.maxRecvKbps
response field based on the state of the switch - Makes a POST request with the updated payload to the
/rest/system/config
endpoint on your Syncthing server
- Makes a GET request with authentication headers to the
You should now be able to deploy this flow and access the new switch.syncthing_rate_limit
entity in Home Assistant.
Home Assistant Configuration
The last piece of the puzzle is to create a sensor which can monitor our current sync progress. This will be accomplished using the Command Line sensor.
Add the following to your configuration.yaml
file:
sensor:
- platform: command_line
command: "curl -X GET -H \"X-API-Key: YourAPIKeyHere\" http://ServerIPAddress:8384/rest/db/status?folder=\"FolderID\""
name: syncthing_sync_remaining
value_template: '{{ (float(value_json.needBytes) / 1073741824) | round(2) }}'
scan_interval: 600
unit_of_measurement: "GiB"
- Replace
YourAPIKeyHere
with your API key - Replace
ServerIPAddress
with the IP address of your Syncthing server - Replace
FolderID
with the ID of the folder you want to monitor
This sensor will use the curl
command to fire a GET request to the Syncthing /rest/db/status
endpoint for your folder every 10 minutes. It then converts the needBytes
field to GiB, and rounds it to two decimal places. This will be exposed to Home Assistant under sensor.syncthing_sync_remaining
.
You can now go ahead and add your new entities to Home Assistant - I like to place my syncthing_sync_remaining
entity in a conditional card that checks for a value of 0.0
. This allows the card to be hidden when not needed.
Conclusions
Using the above configuration, you can integrate Syncthing into home assistant to create some simple controls. You can take this further and use the entities that you create for automations, like pausing or applying rate limiting to Syncthing if a media player is busy streaming.
Feel free to leave any questions in the comments below.