Example: Meraki - Control SSID's and Device Policy via Exec and BASH

I use the Maraki API to control the SSID availability and change a device’s policy.

My use case is:

  1. Turn on/off the guest SSID via Alexa/Openhab
  2. Allow my wife/rules to turn off streaming content and similar when our kids should get back to the real world

You should know how to manipulate your linux environment and secure your installation along with good RBAC controls to your configs.

First section id Device Policy and the second is the SSID control. Bash scripts are kept executable by openhab’s user. I am sure that I could have mapped this via the HTTP binding but I already had the Bash scripts sooo.

_Device Policies - Change network behavior based upon a sitemap _

  • Create items to manipulate/display in the sitemap.
//Device Policy
String	Policy_LivingRoomRoku	"Living Room Roku"
String	Policy_NintendoSwitch	"Nintendo Switch"
String	Policy_BlueNintendo3DS	"Blue 3DS"
String	Policy_RedNintendo3DS	"Red 3DS"
String	Policy_Chromebook		"Chromebook"
String	Policy_AppleTV			"MB AppleTV"
String	Policy_DominicsiPhone	"DominicsiPhone"
String	Policy_OfficeiMac		"Office iMac"
String  Policy_FireTV           "FireTV"
String  Policy_XBOX_ONE         "XBOX ONE"
String  Policy_MB_ROKU          "Master Bedroom Roku"


//device policy normal
Thing exec:command:DevicePolicyExecNormal "Device Policy Normal" [command="sudo /home/openhabian/meraki_client_policy_Normal %2$s", interval=0, timeout=5, autorun=false]
//device policy blocked
Thing exec:command:DevicePolicyExecBlocked "Device Policy Blocked" [command="sudo /home/openhabian/meraki_client_policy_Blocked %2$s", interval=0, timeout=5, autorun=false]
//device group policy 102
Thing exec:command:GroupPolicyExec102 "Group Policy 102" [command="sudo /home/openhabian/meraki_client_policy_PolicyNum102 %2$s", interval=0, timeout=5, autorun=false]
//device group policy 103
Thing exec:command:GroupPolicyExec103 "Group Policy 103" [command="sudo /home/openhabian/meraki_client_policy_PolicyNum103 %2$s", interval=0, timeout=5, autorun=false]
//device group policy 104
Thing exec:command:GroupPolicyExec104 "Group Policy 104" [command="sudo /home/openhabian/meraki_client_policy_PolicyNum104 %2$s", interval=0, timeout=5, autorun=false]
//device group policy 105
Thing exec:command:GroupPolicyExec105 "Group Policy 105" [command="sudo /home/openhabian/meraki_client_policy_PolicyNum105 %2$s", interval=0, timeout=5, autorun=false]


  • Maraki has two default policies Normal and Blocked then your group policies are exposed in order of creation by 102,103, etc - u can find the # of your policy by editing the policy within the Meraki portal and looking at the url in your browser:
//Device Policy Normal
String     DevicePolicyExecNormal_Output          "Output"           {channel="exec:command:DevicePolicyExecNormal:output"}
String     DevicePolicyExecNormal_Input           "Input"            {channel="exec:command:DevicePolicyExecNormal:input"}
Number     DevicePolicyExecNormal_Exit            "Exit Value"       {channel="exec:command:DevicePolicyExecNormal:exit"}
Switch     DevicePolicyExecNormal_Run             "Running"          {channel="exec:command:DevicePolicyExecNormal:run"}
DateTime   DevicePolicyExecNormal_Lastexecution   "Last Execution"   {channel="exec:command:DevicePolicyExecNormal:lastexecution"}
//Device Policy Blocked
String     DevicePolicyExecBlocked_Output          "Output"           {channel="exec:command:DevicePolicyExecBlocked:output"}
String     DevicePolicyExecBlocked_Input           "Input"            {channel="exec:command:DevicePolicyExecBlocked:input"}
Number     DevicePolicyExecBlocked_Exit            "Exit Value"       {channel="exec:command:DevicePolicyExecBlocked:exit"}
Switch     DevicePolicyExecBlocked_Run             "Running"          {channel="exec:command:DevicePolicyExecBlocked:run"}
DateTime   DevicePolicyExecBlocked_Lastexecution   "Last Execution"   {channel="exec:command:DevicePolicyExecBlocked:lastexecution"}
//Device Policy 102
String     GroupPolicy102_Output          "Output"           {channel="exec:command:GroupPolicyExec102:output"}
String     GroupPolicy102_Input           "Input"            {channel="exec:command:GroupPolicyExec102:input"}
Number     GroupPolicy102_Exit            "Exit Value"       {channel="exec:command:GroupPolicyExec102:exit"}
Switch     GroupPolicy102_Run             "Running"          {channel="exec:command:GroupPolicyExec102:run"}
DateTime   GroupPolicy102_Lastexecution   "Last Execution"   {channel="exec:command:GroupPolicyExec102:lastexecution"}
//Device Policy 103
String     GroupPolicy103_Output          "Output"           {channel="exec:command:GroupPolicyExec103:output"}
String     GroupPolicy103_Input           "Input"            {channel="exec:command:GroupPolicyExec103:input"}
Number     GroupPolicy103_Exit            "Exit Value"       {channel="exec:command:GroupPolicyExec103:exit"}
Switch     GroupPolicy103_Run             "Running"          {channel="exec:command:GroupPolicyExec103:run"}
DateTime   GroupPolicy103_Lastexecution   "Last Execution"   {channel="exec:command:GroupPolicyExec103:lastexecution"}
//Device Policy 104
String     GroupPolicy104_Output          "Output"           {channel="exec:command:GroupPolicyExec104:output"}
String     GroupPolicy104_Input           "Input"            {channel="exec:command:GroupPolicyExec104:input"}
Number     GroupPolicy104_Exit            "Exit Value"       {channel="exec:command:GroupPolicyExec104:exit"}
Switch     GroupPolicy104_Run             "Running"          {channel="exec:command:GroupPolicyExec104:run"}
DateTime   GroupPolicy104_Lastexecution   "Last Execution"   {channel="exec:command:GroupPolicyExec104:lastexecution"}
//Device Policy 105
String     GroupPolicy105_Output          "Output"           {channel="exec:command:GroupPolicyExec105:output"}
String     GroupPolicy105_Input           "Input"            {channel="exec:command:GroupPolicyExec105:input"}
Number     GroupPolicy105_Exit            "Exit Value"       {channel="exec:command:GroupPolicyExec105:exit"}
Switch     GroupPolicy105_Run             "Running"          {channel="exec:command:GroupPolicyExec105:run"}
DateTime   GroupPolicy105_Lastexecution   "Last Execution"   {channel="exec:command:GroupPolicyExec105:lastexecution"}


  • Rules to implement changes when a new policy is selected via the sitemap. This could be shortened by the bash script using multiple variables but just havent gotten around to changing it (hint hint)

  • MAC address substituted here for each device you want to control by policy. MAC is right there in the Meraki portal -copy paste

rule "Policy_OfficeiMac Actions"
	when Item Policy_OfficeiMac changed
		if (Policy_OfficeiMac.state == "norm") {
			DevicePolicyExecNormal_Input.sendCommand("ff:ff:ff:ff:ff") //mac address of the iMAC
		if (Policy_OfficeiMac.state == "block") {
		if (Policy_OfficeiMac.state == "p_102") { 
		if (Policy_OfficeiMac.state == "p_103") { 
		if (Policy_OfficeiMac.state == "p_104") { 
		if (Policy_OfficeiMac.state == "p_105") { 
//rinse repeat for all devices


  • Simple to make this pin code protected but thats out of scope here
			Group item=Device_Policy_Group {
				Switch item=Policy_OfficeiMac mappings=[norm="Norm",block="Block",p_103="Local",p_105="No Youtube"]

NormalPolicy Bash script

  • The exec item sends the MAC address as the variable

  • <org_id> is your Organization ID that contains the Meraki devices your managing.Read the Meraki API manual

  • <api_key> is the unique api-key for your organization and received after you apply for api access. Consider this as important as securing your root access password i.e. where the bash script is and the users/groups that can read.

  • Change “normal” to “blocked” for …well you know.


curl -L -H 'X-Cisco-Meraki-API-Key:<api_key>' -X PUT -H'Content-Type: application/json'  --data-binary '{"devicePolicy":"normal"}' $merakiurl
exit 0


  • Same elements as above with the exception of the Curl data payload is different for the non-default policies.

  • Change “groupPolicyId”: 102 to 103 etc etc for each of the policies you would like to control


curl -L -H 'X-Cisco-Meraki-API-Key:<api_key>' -X PUT -H'Content-Type: application/json'  --data-binary '{"devicePolicy": "group","groupPolicyId": 102}' $merakiurl
exit 0

SSID Control

  • Same drill as above but you need the SSID # for what you want to control. In Meraki’s portal navigate to the SSID to controls “edit settings” page and the url at the top will have it as"/configure/access_control?ssid_number=0" where the number at the end is the SSID. You can see that you could programatically change your SSID’s daily/hourly etc


Thing exec:command:guestwirelesscontroloff "Guest Wireless Control Off" [command="sudo /home/openhabian/merakissid_1_api_off", interval=0, timeout=5, autorun=false]
Thing exec:command:guestwirelesscontrolon "Guest Wireless Control On" [command="sudo /home/openhabian/merakissid_1_api_on", interval=0, timeout=5, autorun=false]


// Guest Network
Switch Guest_Network_Switch "Guest Network" ["Switchable"]
// Guest Wireless Off
String     GuestWirelessControlOff_Output          "Output"           {channel="exec:command:guestwirelesscontroloff:output"}
String     GuestWirelessControlOff_Input           "Input"            {channel="exec:command:guestwirelesscontroloff:input"}
Number     GuestWirelessControlOff_Exit            "Exit Value"       {channel="exec:command:guestwirelesscontroloff:exit"}
Switch     GuestWirelessControlOff_Run             "Running"          {channel="exec:command:guestwirelesscontroloff:run"}
DateTime   GuestWirelessControlOff_Lastexecution   "Last Execution"   {channel="exec:command:guestwirelesscontroloff:lastexecution"}
// Guest Wireless On
String     GuestWirelessControlOn_Output          "Output"           {channel="exec:command:guestwirelesscontrolon:output"}
String     GuestWirelessControlOn_Input           "Input"            {channel="exec:command:guestwirelesscontrolon:input"}
Number     GuestWirelessControlOn_Exit            "Exit Value"       {channel="exec:command:guestwirelesscontrolon:exit"}
Switch     GuestWirelessControlOn_Run             "Running"          {channel="exec:command:guestwirelesscontrolon:run"}
DateTime   GuestWirelessControlOn_Lastexecution   "Last Execution"   {channel="exec:command:guestwirelesscontrolon:lastexecution"}


rule "Guest Network"
//	when
		Item Guest_Network_Switch received command
			switch(receivedCommand.toString ){
			case "ON": {
			//sendBroadcastNotification("Turned On the Guest Wireless at " + now,"","warning")
			case "OFF": {
		//sendBroadcastNotification("Turned Off the Guest Wireless at " + now,"","warning")


			Switch item=Guest_Network_Switch label="Guest Wireless Control"

Bash scripts

  • replace “1” in the url below with the SSID # you want to control


curl -L -H 'X-Cisco-Meraki-API-Key:<api_key>' -X PUT -H'Content-Type: application/json' --data-binary '{"enabled":true}' 'https://dashboard.meraki.com/api/v0/networks/<org_id>/ssids/1'
exit 0


curl -L -H 'X-Cisco-Meraki-API-Key:<api_key>' -X PUT -H'Content-Type: application/json' --data-binary '{"enabled":false}' 'https://dashboard.meraki.com/api/v0/networks/<org_id>/ssids/1'
exit 0```

rinse repeat for other SSID's

Of course. I don’t use Meraki but others do.
Share away!

This is fantastic I have an issue that I need policies that I need to apply at different times of the day

6am - 8am block (getting ready for school time)
8am - 8.30 allow (free time before school)
3.30 - 5. No YouTube (homework time)
5-8.30 allow (free time)
8.30 - 10.30 no YouTube
10.30 -12pm block (bedtime)

I’ve asked Meraki and there’s no way of nesting policies - THIS doesn’t nest either but i can schedule policy changes


Do you know if there is a way to check device usage? I’d love to layer in some rules based on usage tracking of a device - either by total or by category (eg 1 hour YouTube then block)

I believe so - havent looked at the API in awhile - ill see if i can look this week and if I make something up ill add it to the thread.

Hi @Gordon_Geist - hope you can help me get going?

I have tried to get a sample of what you have nicely detailed above but the difference being that I dont use thing files so I made one in paperUI (this itself might be the issue but I think that bit is OK.



String	Policy_Jackson			"Jackson Meraki"

//Device Policy 103
String     GroupPolicy103_Output          "Output"           {channel="exec:command:GroupPolicyExec103:output"}
String     GroupPolicy103_Input           "Input"            {channel="exec:command:GroupPolicyExec103:input"}
Number     GroupPolicy103_Exit            "Exit Value"       {channel="exec:command:GroupPolicyExec103:exit"}
Switch     GroupPolicy103_Run             "Running"          {channel="exec:command:GroupPolicyExec103:run"}
DateTime   GroupPolicy103_Lastexecution   "Last Execution"   {channel="exec:command:GroupPolicyExec103:lastexecution"}



curl -L -H 'X-Cisco-Meraki-API-Key:efcecb50580adxxxxxxxxx6b99e3fe98382f6420' -X PUT -H'Content-Type: application/json'  --data-binary '{"devicePolicy": "group","groupPolicyId": 103}' $merakiurl
exit 0  ```

rule file

rule "Policy_Jackson Actions"
	when Item Policy_Jackson changed
		if (Policy_Jackson.state == "norm") {
			DevicePolicyExecNormal_Input.sendCommand("zz:zz:zz:zz:zz:zz")  //zz is real mac
		if (Policy_Jackson.state == "block") {
		if (Policy_Jackson.state == "p_103") { 
//rinse repeat for all devices


Switch item=Policy_Jackson mappings=[norm="Norm",block="Block",p_103="Local"]

trying to munge it together on a command line as one curl command it shows “page not found”

any ideas?

So sorry I didn’t see this. My system was in the “not broken so don’t fix”. Just got back on because I borked the system (for exactly that problem - it wasn’t broken but I did a blanket apt-get upgrade and bam wouldn’t boot and couldn’t get it past a certain out of order process starts).

I use UNIFI now and was in the process of adapting/building a way to do similar to the Meraki. My Meraki APs basically got flaky (bad placement by me) so I haven’t been running the scripts

Hmm looked at the API to see if there was a breaking change and didn’t see anything The problem I had when putting this and similar curl commands together was the way the scripting language handles typing if I remember correctly.